106 lines
3.3 KiB
Markdown
106 lines
3.3 KiB
Markdown
Currently, Info about a Host's Properties is manually gathered and
|
|
propigated. propertyList combines the Info of the Properties in the list;
|
|
Docker.docked extracts relevant Info from the Properties of the container
|
|
(but not al of it, intentionally!); etc.
|
|
|
|
This works, but it's error-prone. 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. Of
|
|
course, only one of them will be run, and only its info should be
|
|
propigated out..
|
|
|
|
----
|
|
|
|
Currently, ensureProperty detects if it's called on a property with a
|
|
non-empty Info, and prints a warning. Would prefer to handle this at the
|
|
type level though..
|
|
|
|
----
|
|
|
|
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
|
|
|
|
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.
|
|
|
|
----
|
|
|
|
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 is able to add foo and bar's Info to the info of the
|
|
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.
|
|
|
|
Also it has the problem that it doesn't support this:
|
|
|
|
property "demo" = do
|
|
needfoo <- liftIO checkFoo
|
|
if needfoo
|
|
then do
|
|
foop <- liftIO getFooParam
|
|
ensureProperty (foo foop)
|
|
else ensureProperty bar
|
|
|
|
----
|
|
|
|
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 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.)
|