summaryrefslogtreecommitdiff
path: root/doc/todo/differential_update_via_RevertableProperty.mdwn
blob: 79afebe44df3f12cab9f558c8c85c80217570799 (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
Long ago, nomeata pointed out that RevertableProperty required the user to
keep track of different versions of a Host, in a way that should be able to
be automated. When the user decides to revert a RevertableProperty, they
have to keep the reverted property on the Host until propellor runs there,
and only then can remove it.

What if instead, there was a way to store the old version of a Host
somewhere. Let's not worry about where or how, but assume we have 
`(old, new) :: (Host, Host)`

Propellor could compare `old` and `new`, and if it finds a
RevertableProperty in `old` that is not in `new`, add it in reverted form
to `new'`.

Also, if propellor finds a Property in `old` that is not in `new`, it can
tell the user that this Property needs to be reverted, but cannot be, so
`new` won't fully describe the state of the host. --[[Joey]]

----

There are a lot of ways such a capability could be used, especially if
there were a way to pull the old version of a Host out of a previous
version of config.hs or something like that. But leaving aside such magic,
here are some nice use cases:

* Suppose we want to generate several disk images, which are somewhat
  similar, but differ in some properties. Rather than building a separate
  chroot for each, we can build a chroot for the first, update the first
  disk image, compare that with the second and update the chroot
  accordingly, and so on.
* When propellor is used to build a OS installer disk image, that installer
  could know the properties used to create it, and the properties of the
  system that is desired to be installed. To install, it can rsync the
  installer disk contents to `/target` and then run propellor in `/target`,
  differentially updating it as needed.

----

Here's the catch: It can't be implemented currently! The comparison of
properties needs an `Eq` instance for Property (and RevertableProperty).
But, a property contains an action in the IO monad, which can't have an
`Eq` instance, and so there's no good way to compare properties.

Making propellor use an ESDL could get us `Eq`. But it would make it rather
clumsy to write properties, something like this. 

<pre>
appendfoo f = WriteFile f (ListAppend "foo" (ReadFile f))
</pre>

(Perhaps a deeply embedded DSL would be better.)

Could a Free monad get us `Eq`? Well, there can apparently be free monads that
have an `Eq` instance, but I tried building one for a simple teletype, and
failed, which does not bode well. Here's the code; this fails to compile
because of a missing instance `(Eq1 ((->) String))`, and of course comparing
functions for equality is not generally feasible.

<pre>
{-# LANGUAGE FlexibleContexts, UndecidableInstances #-}

import Control.Monad.Free
import Prelude.Extras

data TeletypeF x
        = PutStrLn String x
        | GetLine (String -> x)

instance Functor TeletypeF where
        fmap f (PutStrLn str x) = PutStrLn str (f x)
        fmap f (GetLine k) = GetLine (f . k)

instance (Eq1 ((->) String)) => Eq1 TeletypeF  where
        PutStrLn a x ==# PutStrLn b y = a == b && x == y
        GetLine a ==# GetLine b = a ==# b

type Teletype = Free TeletypeF

putStrLn' :: String -> Teletype ()
putStrLn' str = liftF $ PutStrLn str ()

getLine' :: Teletype String
getLine' = liftF $ GetLine id

foo :: Teletype ()
foo = do
        putStrLn' "name?"
        name <- getLine'
        putStrLn' ("hello, " ++ name)

fooisfoo :: Bool
fooisfoo = foo ==# foo
</pre>