summaryrefslogtreecommitdiff
path: root/doc/todo/info_propigation_out_of_nested_properties.mdwn
blob: 536d6719279be93f97cbaf77b0bd6c86900801cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
> Now [[fixed|done]]!! --[[Joey]]

Currently, Info about a Host's Properties is propigated to the host by
examining the tree of Properties.

This works, but there's one problem. Consider this example:

	withOS desc $ \o -> case o of
                (Just (System (Debian Unstable) _)) -> ensureProperty foo
		_ -> ensureProperty bar

Here, the Info of `foo` is not propigated out. Nor is `bar`'s Info.
It's not really clear if just one Info, or both should be propigated out.

----

One approach might be to make the Propellor monad be able to be run in two
modes. In run mode, it actually performs IO, etc. In introspection mode, all
liftIO is a no-op, but all Info encountered is accumulated using a Reader.
This might need two separate monad definitions.

That is surely doable, but consider this example:

	property "demo" = do
		needfoo <- liftIO checkFoo
		if needfoo
			then ensureProperty foo
			else ensureProperty . bar =<< liftIO (getBarParam)

In introspection mode, the liftIO is a no-op, but needs to return a Bool.
That seems unlikely (how to pick which?), but even if some defaulting is
used, only one of foo or bar's info will be seen.

Worse, the bar property is not fully known until IO can be performed to get
its parameter.

----

Another approach could be something like this:

	withInfoFrom foo $ \callfoo ->
		withInfoFrom bar $ \callbar ->
			property "demo" = do
				needfoo <- liftIO checkFoo
				if needfoo
					then callfoo
					else callbar

Here withInfoFrom adds foo and bar as child properties of the demo property
that (may) call them.

This approach is not fully type safe; it would be possible to call
withInfoFrom in a way that didn't let it propigate the info.

And again this doesn't solve the problem that IO can be needed to get
a parameter of a child property.

----

Another approach would be to add a new SimpleProperty, which is a property
that has no Info. Only allow calling ensureProperty on this new type.

(Or, remove propertyInfo from Property, and add a new InfoProperty that
has the info.)

But, propertyList can only contain one type at a time,
not a mixed list of Property and SimpleProperty.

Could a GADT be used instead?

	{-# LANGUAGE GADTs #-}
	{-# LANGUAGE EmptyDataDecls #-}

	data HasInfo
	data NoInfo

	data Property = IProperty (GProperty HasInfo) | SProperty (GProperty NoInfo)

	data GProperty i where
		GIProperty :: Desc -> Propellor Result -> Info -> GProperty HasInfo
		GSProperty :: Desc -> Propellor Result -> GProperty NoInfo

	ensureProperty :: GProperty NoInfo -> Propellor Result
	ensureProperty (GSProperty d r) = r

That works. I made a `gadtwip` git branch that elaborated on that, 
to the point that Property.File compiles, but is otherwise 
unfinished. Most definitions of `Property` need to be changed to
`GProperty NoInfo`, so that ensureProperty can call them. It's a big,
intrusive change, and it may complicate propellor too much. 

I've tried to make this change a couple times now, and not been completely
successful so far.

(I may need to make instances of Prop for `GProperty NoInfo` and `GProperty
HasInfo`, if that's possible, and make more Property combinators work on
Prop.)