Last active
October 26, 2017 22:35
-
-
Save expede/8712eba17daa3980e352 to your computer and use it in GitHub Desktop.
What is the Free Monad + Interpreter pattern?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Just the code from http://programmers.stackexchange.com/a/242803 | |
-- ...I find it useful to type this stuff out. Weird, I know. | |
data DSL next = Get String (String -> next) | |
| Set String String next | |
| End | |
p1 :: DSL (DSL (DSL next)) | |
p1 = Get "foo" $ \foo -> Set "bar" foo End | |
-- Only one possible derrivation. Can be derived with the DeriveFunctor GHC extension | |
instance Functor DSL where | |
f `fmap` (Get name k) = Get name (f . k) | |
f `fmap` (Set name value next) = Set name value (f next) | |
f `fmap` End = End | |
data Free f a = Free (f (Free f a)) | Return a | |
data List a = Cons a (List a) | Nil | |
data Free DSL a = Free (DSL (Free DSL a)) | Return a | |
p2 :: Free DSL a | |
p2 = Free (Get "foo" $ \foo -> Free (Set "bar" foo (Free End))) | |
instance Functor f => Monad (Free f) where | |
return = Return | |
Free a >>= f = Free ((>>= f) `fmap` a) | |
Return a >>= f = f a | |
p3 = do foo <- Free (Get "foo" Return) | |
Free (Set "bar" foo (Return ())) | |
Free End | |
liftFree :: Functor f => f a -> Free f a | |
liftFree action = Free (Return `fmap` action) | |
get key = liftFree (Get key id) | |
set key value = liftFree (Set key value ()) | |
end = liftFree End | |
p4 :: Free DSL a | |
p4 = do foo <- get "foo" | |
set "bar" foo | |
end | |
-- p4 = Free (Get "foo" $ \ foo -> Free (Set "bar" foo (Free End))) | |
follow :: String -> Free DSL String | |
follow key = do key' <- get key | |
get key' | |
p5 = do foo <- follow "foo" | |
set "bar" foo | |
end | |
-- INTERPRETER | |
-- =========== | |
-- (Hypothetical data store) | |
runIO :: Free DSL a -> IO () | |
runIO (Free (Get key k)) = do res <- getKey key | |
runIO $ k res | |
runIO (Free (Set key value next)) = do setKey key value | |
runIO next | |
runIO (Free End) = close | |
runIO (Return _) = error "Should not be reachable" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment