summaryrefslogtreecommitdiff
path: root/doc/todo
diff options
context:
space:
mode:
authorJoey Hess2017-07-13 20:28:45 -0400
committerJoey Hess2017-07-13 20:28:45 -0400
commit83f7e1ccb34001cbc3c60ba5a6693f65e7abc880 (patch)
tree0c349e1f67c2cfad6259071167c709b02f0eea2e /doc/todo
parente780ed549ddb9861d7a3c75ae03e2ba9d27a80e4 (diff)
idea
Diffstat (limited to 'doc/todo')
-rw-r--r--doc/todo/differential_update_via_RevertableProperty.mdwn93
1 files changed, 93 insertions, 0 deletions
diff --git a/doc/todo/differential_update_via_RevertableProperty.mdwn b/doc/todo/differential_update_via_RevertableProperty.mdwn
new file mode 100644
index 00000000..79afebe4
--- /dev/null
+++ b/doc/todo/differential_update_via_RevertableProperty.mdwn
@@ -0,0 +1,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>