I have a state where it contains different values and I want to use the same function to update different values in the state. But I am unable to do so because of the following error.
Error Received:
warning: [-Woverlapping-patterns]
Pattern match is redundant
In a case alternative: currentRegister -> ...
|
559 | currentRegister -> modify $ \st->st{ currentRegister = nextAddr}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
data St = St
{ cotxt :: [Cxt]
, currentLabel :: Int
, currentLogicLabel :: Int
, currentIfLabel :: Int
, currentWhileLabel :: Int
, currentRegister :: Int
}
getcurrStateValue :: (St -> Int) -> Compile Int
getcurrStateValue x = do
current <- gets x
let nextAddr = current + 1
case x of
currentLabel -> modify $ \st->st{ currentLabel = nextAddr}
currentRegister -> modify $ \st->st{ currentRegister = nextAddr}
currentLogicLabel -> modify $ \st->st{ currentLogicLabel = nextAddr}
currentIfLabel -> modify $ \st->st{ currentIfLabel = nextAddr}
currentWhileLabel -> modify $ \st->st{ currentWhileLabel = nextAddr}
return current
func1::
..
current <- getcurrStateValue currentRegister
..
func2::
anothercurrent <- getcurrStateValue currentLogicLabel ```
As commenters have pointed out, in this case expression, you’re trying to compare the parameter x with the getter functions from the St type.
case x of
currentLabel -> modify $ \ st -> st { currentLabel = nextAddr }
currentRegister -> modify $ \ st -> st { currentRegister = nextAddr }
-- …
Unfortunately, functions cannot be compared; only data constructors and numeric literals (Num + Eq) can appear in basic patterns. What your code actually does is match x against anything, and bind it to a new local variable currentLabel, and then all the subsequent case branches (currentRegister and so on) are redundant, as the warning tells you.
It’s just as if you had written this, with distinct variable names:
case x of
a -> modify $ \ st -> st { currentLabel = nextAddr }
b -> modify $ \ st -> st { currentRegister = nextAddr }
-- …
A simple solution here is to add a new enumeration type indicating which field you want to modify, and pattern-match on that:
data Field
= CurrentLabel
| CurrentLogicLabel
| CurrentIfLabel
| CurrentWhileLabel
| CurrentRegister
getCurrStateValue :: Field -> Compile Int
getCurrStateValue field = case field of
CurrentLabel -> do
current <- gets currentLabel
modify $ \ st -> st { currentLabel = current + 1 }
pure current
CurrentRegister -> do
current <- gets currentRegister
modify $ \ st -> st { currentRegister = current + 1 }
pure current
CurrentLogicLabel -> do
current <- gets currentLogicLabel
modify $ \ st -> st { currentLogicLabel = current + 1 }
pure current
CurrentIfLabel -> do
current <- gets currentIfLabel
modify $ \ st -> st { currentIfLabel = current + 1 }
pure current
CurrentWhileLabel -> do
current <- gets currentWhileLabel
modify $ \ st -> st { currentWhileLabel = current + 1 }
pure current
As you can see, though, this is quite repetitive! A better solution is to pass both a getter and a setter function (whereas currently you’re only passing the getter):
getCurrStateValue
:: (St -> Int)
-> (St -> Int -> St)
-> Compile Int
getCurrStateValue getField setField = do
current <- gets getField
modify $ \ st -> setField st $ current + 1
pure current
Of course, now this has moved the repetitiveness to the call site, since you must pass both functions, and they must refer to the same field:
current <- getCurrStateValue
currentRegister
(\ st x -> st { currentRegister = x })
So an improvement would be to package up these invocations into helper functions for each field:
getCurrRegister = getCurrStateValue
currentRegister
(\ st x -> st { currentRegister = x })
getCurrLogicLabel = getCurrStateValue
currentLogicLabel
(\ st x -> st { currentLogicLabel = x })
I think this is a good place to stop: the repetition is abstracted away without too much heavy machinery.
However, this can be abstracted further. The combination of a getter and setter for the same field is very useful, and it would be nice to reuse it with other functions besides getCurrStateValue.
A getter+setter pair can be packaged up into a lens (or more generally an “optic”), which is a first-class accessor that can be composed with other lenses. While there are many lens libraries like lens (big and complete) and microlens (small and simple) containing functions for working with lenses, you don’t actually need any dependencies to define a lens; it’s just a function with a type like this:
(Functor f) => (a -> f b) -> s -> f t
Or, slightly simplified:
(Functor f) => (a -> f a) -> s -> f s
How this works is beyond the scope of this answer, but what it means is an accessor for a field of type a within a structure of type s. In your case, a is always Int and s is St. Here’s a helper function for defining a lens from a getter and setter:
lens
:: Functor f
=> (s -> a) -- get ‘a’ out of ‘s’
-> (s -> a -> s) -- put ‘a’ into ‘s’
-> (a -> f a) -> s -> f s -- lens
lens getter setter
= \ f s -> fmap (setter s) (f (getter s))
Using this, you can define lenses for your fields:
currentRegisterL, currentLogicLabelL -- …
:: (Functor f) => (Int -> f Int) -> St -> f St
currentRegisterL = lens
currentRegister
(\ st x -> st { currentRegister = x })
currentLogicLabelL = lens
currentLogicLabel
(\ st x -> st { currentLogicLabel = x })
-- …
And then use them with functions and types from lens, such as view to use the getter part and set to use the setter. To take a lens as a function parameter, you need the RankNTypes extension, which allows passing polymorphic functions like lenses as arguments to other functions.
{-# LANGUAGE RankNTypes #-}
import Control.Lens (Lens', set, view)
getCurrStateValue :: Lens' St Int -> Compile Int
getCurrStateValue field = do
current <- gets $ view field
modify $ set field $ current + 1
pure current
Most of the time, when using lenses, people don’t write their own by hand, unless they specifically want to avoid a dependency on a lens package. Instead, it’s common to automate this by deriving lenses for a data type with Template Haskell:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens.TH (makeLenses)
data St = St
{ _cotxt :: [Cxt]
, _currentLabel :: Int
, _currentLogicLabel :: Int
, _currentIfLabel :: Int
, _currentWhileLabel :: Int
, _currentRegister :: Int
}
makeLenses ''St
The convention is that the getters and record accessors are prefixed with an underscore, like _currentLabel, and the lenses derived from them have no prefix, like currentLabel. You can still use the accessor functions directly, but when you need to abstract over field access, you can use view, set, over, and other such functions.
Pattern matching only works with data constructors; it can't check for equality of values. (Patterns like x:xs, [x,y], and (x,y) are treated constructor matches.) In particular, there is no other general way to handle coproduct types like data Foo = Bar | Baz. (Aside from using Eq, Ord, etc.)
What you probably need here is a lens.
Related
I'm writing a GHC Core plugin that eventually adds new function declarations to the module it's called on.
Currently I'm struggling with combining the Core helpers to produce an Expr corresponding to a lambda function.
For example, say we want to synthesize the term \(x :: Double) -> x + x.
If we look at the pretty-printed Core of that :
f \ (x [Dmd=<S,U(U)>] :: Double) -> plusDouble x x
After copying the AST of that term into a Showable one we can see its structure of lambda abstractions and function applications , which I aim to reproduce by hand using Core combinators :
DLam "x::Double"
(DApp
(DApp (DVar "plusDouble::Double -> Double -> Double") (DVar "x::Double")) (DVar "x::Double"))
Long story short, I'm not able to synthesize that term L0L.
I produce a term that sort of looks right (?), GHC completes with no errors after splicing in my new term, the module with the new declaration loads correctly but GHCi crashes when I try using it.
My question(s) :
why do those empty (DEFAULT) cases appear? I suspect they are due to how I use mkCoreApps
how do I box the result such that it's a Double instead of a primitive Double# ?
GHCi signature of the new declaration :
PluginTest.f_new :: Double -> ghc-prim-0.7.0:GHC.Prim.Double#
pretty Core :
f_new \ (x :: Double) ->
case x of x_ { D# x_ ->
case x_ of wild_00 { __DEFAULT ->
(case x_ of wild_00 { __DEFAULT -> +## wild_00 }) wild_00
}
}
Show instance of above :
DLam "x::Double"
(DCase (DVar "x::Double") "Double#" [
("D#",["x_"], DCase (DVar "x_::Double") "Double#" [
("__DEFAULT",[], DApp (DCase (DVar "x_::Double") "Double# -> Double#" [
("__DEFAULT",[], DApp (DVar "+##::Double# -> Double# -> Double#") (DVar "wild_00::Double#"))]) (DVar "wild_00::Double#"))])])
Full repro steps and code :
I reach into the ghc 9.0.1 API and after declaring imports and a couple helpers :
import GHC.Core.Make (mkCoreLams, mkSingleAltCase, mkCoreApps, mkCoreConApps) -- Core syntax combinators
import qualified GHC.Types.Name.Occurrence as ON (varName, mkOccName)
import GHC.Core.Opt.Monad (CoreM)
import GHC.Types.Unique.Supply (MonadUnique(..))
import GHC.Types.SrcLoc (noSrcSpan)
import GHC.Builtin.Types (manyDataConTy)
import GHC.Types.Id.Info (vanillaIdInfo)
import GHC.Types.Id.Make (mkPrimOpId)
import GHC.Builtin.PrimOps (PrimOp(..))
import GHC.Builtin.Types (doubleTy, floatTy, doubleDataCon, floatDataCon)
import GHC.Core.Utils (exprType)
-- | fresh name using the supply of unique symbols provided by MonadUnique
mkNameM :: String -> CoreM Name
mkNameM n = do
u <- getUniqueM
pure $ mkInternalName u (ON.mkOccName ON.varName n) noSrcSpan
-- | an external (= exported) name
mkExtNameM :: Module -- ^ module that will export this
-> String -> CoreM Name
mkExtNameM modl n = do
u <- getUniqueM
pure $ mkExternalName u modl (ON.mkOccName ON.varName n) noSrcSpan
-- | variable identifier (of multiplicity "Many" which is the default)
mkId :: Name -> Type -> Id
mkId xname tyvar = mkLocalVar VanillaId xname manyDataConTy tyvar vanillaIdInfo
mkGlobalId :: Name -> Type -> Id
mkGlobalId xname tyvar = mkGlobalVar VanillaId xname tyvar vanillaIdInfo
I've also written helpers for declaring binary math operators, which uses the builtin primops and wraps them :
-- | apply an Expr corresponding to a binary operator to two argument expressions
appBin :: CoreExpr -> CoreExpr -> CoreExpr -> CoreExpr
appBin f e1 e2 = mkCoreApps f [e1, e2]
-- | primop corrsponding to (+) :: Double -> Double -> Double
d_add :: CoreExpr
d_add = Var (mkPrimOpId DoubleAddOp)
Now we're ready to write a function that takes the current contents of the module Core (ModGuts), declares fresh variables and appends the new declaration to the module.
This function testAddLambda can be used as an additional Core plugin pass, as shown in the GHC user manual.
I've separated out the part that is supposed to declare the term \(x :: Double) -> x + x.
-- | add a single new binding corresponding to a lambda expression to ModGuts
testAddLambda :: ModGuts -> CoreM ModGuts
testAddLambda guts = do
let
modl = mg_module guts
binds = mg_binds guts
exports = mg_exports guts
xn <- mkNameM "x"
xn_ <- mkNameM "x_"
fn <- mkExtNameM modl "f_new" -- new name to be exported
let
x = mkId xn doubleTy
x_ = mkId xn_ doubleTy
f = mkCoreLams [x] $
mkSingleAltCase (Var x) x_ (DataAlt doubleDataCon) [x_] $
appBin d_add (Var x_) (Var x_)
fty = exprType f -- type of 'f'
fv = mkGlobalId fn fty
fbind = NonRec fv f
fexp = Avail fn
guts' = guts {
mg_binds = binds ++ [fbind]
, mg_exports = exports ++ [fexp]
}
pure guts'
An idiom I use for composing a couple of procedures (with memory) is as follows:
p1 :: State (Int, String) ()
p1 = do
(a, b) <- get
... do something ...
put (a', b)
p2 :: State (Int, String) ()
p2 = do
(a, b) <- get
... do something else ...
put (a, b')
main = do
... initializing a0 b0 ...
print . flip evalState (a0, b0)
. sequence $ replicate 10 p1 ++ repeat p2
However, as the number of state variable grows, this quickly gets way more verbose than necessary:
p1 :: State (Int, String, Bool, Int, String, Bool) ()
p1 = do
(a, b, c, d, e, f) <- get
... do something ...
put (a, b, c', d, e, f')
p2 :: State (Int, String, Bool, Int, String, Bool) ()
p2 = do
(a, b, c, d, e, f) <- get
... do something ...
put (a', b', c, d, e, f)
main = do
print . flip evalState (a0, b0, c0, d0, e0, f0)
. sequence $ replicate 10 p1 ++ repeat p2
As I was wondering, is there a way of updating only a few state variables without having to refer to all the unused ones? I was thinking something like IORef but for State (in fact there is a package stateref), but I'm not sure if there are already some common idioms that other people have been using.
This seems like a job for lenses. Especially the Control.Lens.Tuple module together with .= and use:
p1 = do
a <- use _1
-- do something --
_1 .= a'
However, it's usually better if you give the things in your state proper names, e.g.
{-# LANGUAGE TemplateHaskell #-
data Record = MkRecord { _age :: Int
, _name :: String
, _programmer :: Bool
} deriving (Show, Eq)
makeLenses ''Record
That way, you have better names for your field:
p1 = do
a <- use age
-- do something --
age .= a'
Note that this still helps you if you don't want to use lenses, since you can use record syntax to update your data:
p1 = do
r <- get
let a = _age r
--- do something
put $ r{_age = a'}
This is a good situation to use records, with the gets and modify functions to manipulate subparts of the state:
data Env = Env
{ envNumber :: Int
, envText :: String
}
p1 :: State Env ()
p1 = do
a <- gets envNumber
-- ...
modify $ \r -> r { envNumber = a' }
p2 :: State Env ()
p2 = do
b <- gets envText
-- ...
modify $ \r -> r { envText = b' }
gets turns a pure getter function into a state action:
gets :: (s -> a) -> State s a
envNumber :: Env -> Int
gets envNumber :: State Env Int
And modify turns a pure update function into a state action:
modify :: (s -> s) -> State s ()
(\r -> r { envText = b' }) :: Env -> Env
modify (\r -> ...) :: State Env ()
lens's zoom combinator lifts a computation in a State monad into a computation that runs in a "larger" State monad.
zoom :: Lens' s t -> State t a -> State s a
So, given a "big" state:
data Big = Big {
_big1 :: Medium,
_big2 :: Medium
}
data Medium = Medium {
_medium1 :: Small,
_medium2 :: Small
}
data Small = Small { _small :: Int }
makeLenses ''Big
makeLenses ''Medium
makeLenses ''Small
you can "zoom in" on a part of the state:
incr :: State Int ()
incr = id += 1
incrSmall :: State Big ()
incrSmall = zoom (big2.medium1.small) incr
Of course, this'll work on big tuples as well as records, using lens's built-in tuple field accessors.
zoom's real type signature is more general than the simple one I quoted above. It uses MonadState constraints to work under a monad transformer stack, rather than in State specifically.
Given the data structure:
data CustomError = FooError | BarError deriving Show
And then two functions that perform IO:
foo :: IO (Either CustomError Int)
foo = return $ Right 100
bar :: IO (Either CustomError Int)
bar = return $ Left BarError
And a method that adds two Either's.
add :: Either CustomError Int -> Either CustomError Int -> Either CustomError Int
add e1 e2 = (+) <$> e1 <*> e2
Lastly, a function that performs two IO actions, and then tries to apply add to their extracted Right values:
f :: IO (Either CustomError Int)
f = do
x <- foo
y <- bar
return $ add x y
Running it shows:
λ: f
Left BarError
But, let's say that calling foo persists data to a database. If foo succeeds, but bar fails, then there will be irregular state. In other words, I want f to operate like a transaction - everything succeeds or nothing does.
I thought of doing something like:
fWithRecovery:: IO (Either CustomError Int)
fWithRecovery = do
x <- foo
y <- bar
case y of (Right _) -> return $ add x y
(Left FooError) -> fmap Right recoverFoo
(Left BarError) -> fmap Right recoverBar
recoverFoo :: IO Int
recoverFoo = do
_ <- undefined -- clean up DB
return 666 -- using an 'evil' value
-- Note - I know using this value is horrible, but
-- I'm using for this simple example
recoverBar :: IO Int
recoverBar = do
_ <- undefined -- clean up DB
return 42 -- Note - I know using this value is horrible, but
-- I'm using for this simple example
But, I'm curious if there's an idiomatic way to handle roll-back for my do notation case.
1) I need to pass a field constructor parameter to a function. I made some tests but i was unable to do so. Is it possible? Otherwise, is it possible with lens package?
2) Is it possible in a MonadState to modify a field using modify? (I made a few attempts, but without success. For example: modify (second = "x") does not work.
import Control.Monad.State
data Test = Test {first :: Int, second :: String} deriving Show
dataTest = Test {first = 1, second = ""}
test1 = runStateT modif1 dataTest -- OK
test2 = runStateT (modif2 "!") dataTest -- OK
test3 = runStateT (modif3 second) dataTest -- WRONG
-- modif1 :: StateT Test IO ()
modif1 = do
st <- get
r <- lift getLine
put $ st {second = "x" ++ r}
-- modif2 :: String -> StateT Test IO ()
modif2 s = do
stat <- get
r <- lift getLine
put $ stat {second = "x" ++ r ++ s}
-- modif3 :: ???? -> StateT Test IO ()
modif3 fc = do
stat <- get
r <- lift getLine
put $ stat {fc = "x" ++ r}
-- When i try to load the module, this is the result:
-- ghc > Failed:
-- ProvaRecord.hs:33:16:`fc' is not a (visible) constructor field name
As you said, you're probably looking for lenses. A lens is a value that allows to read, set or modify a given field. Usually with Control.Lens, you define fields with underscores and you use makeLenses to create full-featured lenses.
There are many combinators that allow lenses to be used together within MonadState. In your case we can use %=, which in this case would be specialized to type
(MonadState s m) => Lens' s b -> (b -> b) -> m ()
which modifies a state value using a given lens and a function that operates on the inside value.
Your example could be rewritten using lenses as follows:
{-# LANGUAGE TemplateHaskell, RankNTypes #-}
import Control.Lens
import Control.Monad.State
data Test = Test { _first :: Int
, _second :: String
}
deriving Show
-- Generate `first` and `second` lenses.
$(makeLenses ''Test)
-- | An example of a universal function that modifies any lens.
-- It reads a string and appends it to the existing value.
modif :: Lens' a String -> StateT a IO ()
modif l = do
r <- lift getLine
l %= (++ r)
dataTest :: Test
dataTest = Test { _first = 1, _second = "" }
test :: IO Test
test = execStateT (modif second) dataTest
I am trying to abstract the pattern of applying a certain semantics to a free monad over some functor. The running example I am using to motivate this is applying updates to an entity in a game. So I import a few libraries and define a few example types and an entity class for the purposes of this example (I am using the free monad implementation in control-monad-free):
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE TypeFamilies #-}
import Control.Monad.Free
import Control.Monad.Identity
import Control.Monad.Writer
-- Things which can happen to an entity
data Order = Order deriving Show
data Damage = Damage deriving Show
class Entity a where
evolve :: Double -> a -> a
order :: Order -> a -> a
damage :: Damage -> a -> a
-- Make a trivial entity for testing purposes
data Example = Example deriving Show
instance Entity Example where
evolve _ a = a
order _ a = a
damage _ a = a
-- A type to hold all the possible update types
data EntityUpdate =
UpdateTime Double
| UpdateOrder Order
| UpdateDamage Damage
deriving (Show)
-- Wrap UpdateMessage to create a Functor for constructing the free monad
data UpdateFunctor cont =
UpdateFunctor {updateMessage :: EntityUpdate, continue :: cont} deriving (Show, Functor)
-- Type synonym for the free monad
type Update = Free UpdateEntity
I now lift some basic updates into the monad:
liftF = wrap . fmap Pure
updateTime :: Double -> Update ()
updateTime t = liftUpdate $ UpdateTime t
updateOrder :: Order -> Update ()
updateOrder o = liftUpdate $ UpdateOrder o
updateDamage :: Damage -> Update ()
updateDamage d = liftUpdate $ UpdateDamage d
test :: Update ()
test = do
updateTime 8.0
updateOrder Order
updateDamage Damage
updateTime 4.0
updateDamage Damage
updateTime 6.0
updateOrder Order
updateTime 8.0
Now we have the free monad, we need to provide the possibility of different implementations, or semantic interpretations, of monad instance such as test above. The best pattern I can come up with for this is given by the following function:
interpret :: (Monad m, Functor f, fm ~ Free f c) => (f fm -> fm) -> (f fm -> a -> m a) -> fm -> a -> m a
interpret _ _ (Pure _ ) entity = return entity
interpret c f (Impure u) entity = f u entity >>= interpret c f (c u)
Then with some basic semantic functions we can give the two following possible interpretations, one as a basic evaluation and one as a writer monad preforming logging:
update (UpdateTime t) = evolve t
update (UpdateOrder o) = order o
update (UpdateDamage d) = damage d
eval :: Entity a => Update () -> a -> a
eval updates entity = runIdentity $ interpret continue update' updates entity where
update' u entity = return $ update (updateMessage u) entity
logMessage (UpdateTime t) = "Simulating time for " ++ show t ++ " seconds.\n"
logMessage (UpdateOrder o) = "Giving an order.\n"
logMessage (UpdateDamage d) = "Applying damage.\n"
evalLog :: Entity a => Update () -> a -> Writer String a
evalLog = interpret continue $ \u entity -> do
let m = updateMessage u
tell $ logMessage m
return $ update m entity
Testing this in GHCI:
> eval test Example
Example
> putStr . execWriter $ evalLog test Example
Simulating time for 8.0 seconds.
Giving an order.
Applying damage.
Simulating time for 4.0 seconds.
Applying damage.
Simulating time for 6.0 seconds.
Giving an order.
Simulating time for 8.0 seconds.
This all works fine, but it gives me a slightly uneasy feeling that it could be more general, or could be better organised. Having to provide a function to provide the continuation wasn't obvious at first and I'm not sure it is the best approach. I have made several efforts to redefine interpret in terms of functions in the Control.Monad.Free module, such as foldFree and induce. But they all seem to not quite work.
Am I on the right lines with this, or am a making a misjudgement? Most of the articles on free monads I have found concentrate on their efficiency or different ways to implement them, rather than on patterns for actually using them like this.
It also seems desirable to encapsulate this in some kind of Semantic class, so I could simply make different monad instances from my free monad by wrapping the functor in a newtype and making it an instance of this class. I couldn't quite work out how to do this however.
UPDATE --
I wish I could have accepted both answers as they are both extremely informative and thoughtfully written. In the end though, the edit to the accepted answer contains the function I was after:
interpret :: (Functor m, Monad m) => (forall x. f x -> m x) -> Free f a -> m a
interpret evalF = retract . hoistFree evalF
(retract and hoistFree are in Edward Kemmet's free package in Control.Monad.Free).
All three of pipes, operational and sacundim's free-operational package are very relevant and look like they will be very useful for me in the future. Thank you all.
You can use my pipes library, which provides higher level abstractions for working with free monads.
pipes uses free monads to reify every part of the computation:
The Producer of data (i.e. your update) is a free monad
The Consumer of data (i.e. your interpreter) is a free monad
The Pipe of data (i.e. your logger) is a free monad
In fact, they are not three separate free monads: they are all the same free monad in disguise. Once you define all three of them you connect them using pipe composition, (>->), in order to start streaming data.
I'll begin with a slightly modified version of your example that skips the type class you wrote:
{-# LANGUAGE RankNTypes #-}
import Control.Lens
import Control.Proxy
import Control.Proxy.Trans.State
import Control.Monad.Trans.Writer
data Order = Order deriving (Show)
data Damage = Damage deriving (Show)
data EntityUpdate
= UpdateTime Double
| UpdateOrder Order
| UpdateDamage Damage
deriving (Show)
Now what we do is define an Update to be a Producer of EntityUpdates:
type Update r = forall m p . (Monad m, Proxy p) => Producer p EntityUpdate m r
Then we define the actual commands. Each command yields the corresponding update using the respond pipe primitive, which sends the data further downstream for processing.
updateTime :: Double -> Update ()
updateTime t = respond (UpdateTime t)
updateOrder :: Order -> Update ()
updateOrder o = respond (UpdateOrder o)
updateDamage :: Damage -> Update ()
updateDamage d = respond (UpdateDamage d)
Since a Producer is a free monad, we can assemble it using do notation just like you did for your test function:
test :: () -> Update ()
-- i.e. () -> Producer p EntityUpdate m ()
test () = runIdentityP $ do
updateTime 8.0
updateOrder Order
updateDamage Damage
updateTime 4.0
updateDamage Damage
updateTime 6.0
updateOrder Order
updateTime 8.0
However, we can reify the interpreter as a Consumer of data, too. This is nice because we can then directly layer on state over the interpreter instead of using the Entity class you defined.
I'll use a simple state:
data MyState = MyState { _numOrders :: Int, _time :: Double, _health :: Int }
deriving (Show)
begin :: MyState
begin= MyState 0 0 100
... and define some convenient lenses for clarity:
numOrders :: Lens' MyState Int
numOrders = lens _numOrders (\s x -> s { _numOrders = x})
time :: Lens' MyState Double
time = lens _time (\s x -> s { _time = x })
health :: Lens' MyState Int
health = lens _health (\s x -> s { _health = x })
... and now I can define a stateful interpreter:
eval :: (Proxy p) => () -> Consumer (StateP MyState p) EntityUpdate IO r
eval () = forever $ do
entityUpdate <- request ()
case entityUpdate of
UpdateTime tDiff -> modify (time +~ tDiff)
UpdateOrder _ -> modify (numOrders +~ 1 )
UpdateDamage _ -> modify (health -~ 1 )
s <- get
lift $ putStrLn $ "Current state is: " ++ show s
That makes it much more clear what the interpreter is doing. We can see at a glance how it processes incoming values in a stateful way.
To connect our Producer and Consumer we use the (>->) composition operator, followed by runProxy, which transforms our pipeline back to the base monad:
main1 = runProxy $ evalStateK begin $ test >-> eval
... which produces the following result:
>>> main1
Current state is: MyState {_numOrders = 0, _time = 8.0, _health = 100}
Current state is: MyState {_numOrders = 1, _time = 8.0, _health = 100}
Current state is: MyState {_numOrders = 1, _time = 8.0, _health = 99}
Current state is: MyState {_numOrders = 1, _time = 12.0, _health = 99}
Current state is: MyState {_numOrders = 1, _time = 12.0, _health = 98}
Current state is: MyState {_numOrders = 1, _time = 18.0, _health = 98}
Current state is: MyState {_numOrders = 2, _time = 18.0, _health = 98}
Current state is: MyState {_numOrders = 2, _time = 26.0, _health = 98}
You might wonder why we have to do this in two steps. Why not just get rid of the runProxy part?
The reason why is that we may wish to compose more than two things. For example, we can very easily insert a logging stage in between test and eval. I call these intermediate stages Pipes:
logger
:: (Monad m, Proxy p)
=> () -> Pipe p EntityUpdate EntityUpdate (WriterT String m) r
logger () = runIdentityP $ forever $ do
entityUpdate <- request ()
lift $ tell $ case entityUpdate of
UpdateTime t -> "Simulating time for " ++ show t ++ " seconds.\n"
UpdateOrder o -> "Giving an order.\n"
UpdateDamage d -> "Applying damage.\n"
respond entityUpdate
Again, we can very clearly see what logger does: It requests a value, tells a representation of the value, and then passes the value further downstream using respond.
We can insert this in between test and logger. The only thing we must be aware of is that all stages must have the same base monad, so we use raiseK to insert a WriterT layer for eval so that it matches the base monad of logger:
main2 = execWriterT $ runProxy $ evalStateK begin $
test >-> logger >-> raiseK eval
... which produces the following result:
>>> main2
Current state is: MyState {_numOrders = 0, _time = 8.0, _health = 100}
Current state is: MyState {_numOrders = 1, _time = 8.0, _health = 100}
Current state is: MyState {_numOrders = 1, _time = 8.0, _health = 99}
Current state is: MyState {_numOrders = 1, _time = 12.0, _health = 99}
Current state is: MyState {_numOrders = 1, _time = 12.0, _health = 98}
Current state is: MyState {_numOrders = 1, _time = 18.0, _health = 98}
Current state is: MyState {_numOrders = 2, _time = 18.0, _health = 98}
Current state is: MyState {_numOrders = 2, _time = 26.0, _health = 98}
"Simulating time for 8.0 seconds.\nGiving an order.\nApplying damage.\nSimulating time for 4.0 seconds.\nApplying damage.\nSimulating time for 6.0 seconds.\nGiving an order.\nSimulating time for 8.0 seconds.\n"
pipes was designed to solve exactly the kind of problem you describe. A lot of the time we want to reify not only the DSL that generates the data, but the interpreters and intermediate processing stages, too. pipes treats all of these concepts identically and models all of them as connectable stream DSLs. This makes it very easy to swap in and out various behaviors without having to define your own custom interpreter framework.
If you are new to pipes, then you might want to check out the tutorial.
I don't quite understand your example, but I think you are basically reconstructing the operational package in here. Your EntityUpdate type is very much like an instruction set in the sense of operational, and your UpdateFunctor is something like the free functor over the instruction set—which is precisely the construction that relates operational and free monads. (See "Is operational really isomorphic to a free monad?" and this Reddit discussion).
But anyway, the operational package has the function you want, interpretWithMonad:
interpretWithMonad :: forall instr m b.
Monad m =>
(forall a. instr a -> m a)
-> Program instr b
-> m b
This allows you to provide a function that interprets each of the instructions in your program (each EntityUpdate value) as a monadic action, and takes care of the rest.
If I may be allowed a tad of self-promotion, I was just recently writing my own version of operational using free monads, because I wanted to have an Applicative version of operational's Program type. Since your example struck me as being purely applicative, I went through the exercise of writing your evalLog in terms of my library, and I might as well paste it here. (I couldn't understand your eval function.) Here goes:
{-# LANGUAGE GADTs, ScopedTypeVariables, RankNTypes #-}
import Control.Applicative
import Control.Applicative.Operational
import Control.Monad.Writer
data Order = Order deriving Show
data Damage = Damage deriving Show
-- UpdateI is short for "UpdateInstruction"
data UpdateI a where
UpdateTime :: Double -> UpdateI ()
UpdateOrder :: Order -> UpdateI ()
UpdateDamage :: Damage -> UpdateI ()
type Update = ProgramA UpdateI
updateTime :: Double -> Update ()
updateTime = singleton . UpdateTime
updateOrder :: Order -> Update ()
updateOrder = singleton . UpdateOrder
updateDamage :: Damage -> Update ()
updateDamage = singleton . UpdateDamage
test :: Update ()
test = updateTime 8.0
*> updateOrder Order
*> updateDamage Damage
*> updateTime 4.0
*> updateDamage Damage
*> updateTime 6.0
*> updateOrder Order
*> updateTime 8.0
evalLog :: forall a. Update a -> Writer String a
evalLog = interpretA evalI
where evalI :: forall x. UpdateI x -> Writer String x
evalI (UpdateTime t) =
tell $ "Simulating time for " ++ show t ++ " seconds.\n"
evalI (UpdateOrder Order) = tell $ "Giving an order.\n"
evalI (UpdateDamage Damage) = tell $ "Applying damage.\n"
Output:
*Main> putStr $ execWriter (evalLog test)
Simulating time for 8.0 seconds.
Giving an order.
Applying damage.
Simulating time for 4.0 seconds.
Applying damage.
Simulating time for 6.0 seconds.
Giving an order.
Simulating time for 8.0 seconds.
The trick here is the same as in the interpretWithMonad function from the original package, but adapted to applicatives:
interpretA :: forall instr f a. Applicative f =>
(forall x. instr x -> f x)
-> ProgramA instr a -> f a
If you truly need a monadic interpretation it's just a mater of importing Control.Monad.Operational (either the original one or mine) instead of Control.Applicative.Operational, and using Program instead of ProgramA. ProgramA however gives you greater power to examine the program statically:
-- Sum the total time requested by updateTime instructions in an
-- applicative UpdateI program. You can't do this with monads.
sumTime :: ProgramA UpdateI () -> Double
sumTime = sumTime' . viewA
where sumTime' :: forall x. ProgramViewA UpdateI x -> Double
sumTime' (UpdateTime t :<**> k) = t + sumTime' k
sumTime' (_ :<**> k) = sumTime' k
sumTime' (Pure _) = 0
Example usage of sumTime:
*Main> sumTime test
26.0
EDIT: In retrospect, I should have provided this shorter answer. This assumes you're using Control.Monad.Free from Edward Kmett's package:
interpret :: (Functor m, Monad m) =>
(forall x. f x -> m x)
-> Free f a -> m a
interpret evalF = retract . hoistFree evalF