-
Update: in the end I settled on using a Day convolution for the staging, as proposed in "Breadth-First Traversal Via Staging". More specifically, I ended up with I am trying to implement a custom effect to support multi-stage computation. In terms of mtl-style monad transformers, the effect is exactly Below is an experimental implementation (simplified, the actual effect would have data Staged (w :: Type) :: Effect where
Tell :: w -> Staged w m ()
Ask :: (w -> m ()) -> Staged w m ()
type instance DispatchOf (Staged w) = Dynamic
runStaged :: forall w es a. Monoid w => Eff (Staged w : es) a -> Eff es a
runStaged = reinterpret finalise exec
where
finalise :: Eff (Writer (w, w -> Ap (Eff es) ()) : es) a -> Eff es a
finalise m = do
(x, (w, k)) <- runWriter m
x <$ getAp (k w)
exec
:: LocalEnv ls (Writer (w, w -> Ap (Eff es) ()) : es)
-> Staged w (Eff ls) r
-> Eff (Writer (w, w -> Ap (Eff es) ()) : es) r
exec _ (Tell x) = tell @(w, w -> Ap (Eff es) ()) (x, const mempty)
exec env (Ask k) = do
let lk w = Ap (localSeqUnlift env (\unlift -> unlift (k w)))
tell @(w, w -> Ap (Eff es) ()) (mempty, lk) The only problem I see is that I save the result of I have checked that the above code snippet compiles and runs (the example is completely meaningless; it is just a test program): test :: (Staged String :> es, Writer [String] :> es) => Eff es Int
test = do
send (Tell @String "x")
send (Ask \(s :: String) -> tell [s])
send (Tell @String "y")
pure 0 >>> runPureEff (runWriter @[String] (runStaged @String test))
(0,["xy"]) But I want to make sure my use of |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
Not quite. It will break as soon as you try to use properly local effects, because then your code will attempt to run them after they went out of scope. test :: (Staged String :> es, Writer [String] :> es, Reader String :> es) => Eff es Int
test = do
send (Tell @String "x")
send (Ask $ \(s :: String) -> ask >>= \z -> tell [s, z])
send (Tell @String "y")
pure 0
Btw, just switching to SeqForkUnlift in your case won't help anyway because of this line:
You're delaying creation of the unlifting function until This "works": runStaged :: forall w es a. Monoid w => Eff (Staged w : es) a -> Eff es a
runStaged = reinterpret finalise exec
where
finalise :: Eff (Writer (w, w -> Ap (Eff es) ()) : es) a -> Eff es a
finalise m = do
(x, (w, k)) <- runWriter m
x <$ getAp (k w)
exec
:: LocalEnv ls (Writer (w, w -> Ap (Eff es) ()) : es)
-> Staged w (Eff ls) r
-> Eff (Writer (w, w -> Ap (Eff es) ()) : es) r
exec _ (Tell x) = tell @(w, w -> Ap (Eff es) ()) (x, const mempty)
exec env (Ask k) = do
lk <- raise $ localUnlift env SeqForkUnlift $ \unlift -> do
pure $ \w -> Ap (unlift (k w))
tell @(w, w -> Ap (Eff es) ()) (mempty, lk) but it doesn't produce the results you want:
I don't know exactly what you're trying to do, but this doesn't look like the way to do it. |
Beta Was this translation helpful? Give feedback.
Not quite. It will break as soon as you try to use properly local effects, because then your code will attempt to run them after they went out of scope.