Skip to content

Instantly share code, notes, and snippets.

@expede
Last active October 26, 2017 22:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save expede/8712eba17daa3980e352 to your computer and use it in GitHub Desktop.
Save expede/8712eba17daa3980e352 to your computer and use it in GitHub Desktop.
What is the Free Monad + Interpreter pattern?
-- 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