I am trying to create a handler in purescript-run that consumes an effect but uses the same effect. I mean, a handler like the following.
testHandler :: forall r. Run (READER String + r) ~> Run (READER Int + r)
testHandler = interpret (on _reader handler send)
where
handler :: Reader String ~> Run (READER Int + r)
handler = case _ of
Reader k -> do
x <- ask
pure $ k (show x)
This is a handler that converts a READER String effect to a READER Int effect.
However, I get an error on _reader.
Could not match type
( reader :: Reader Int
...
| r6
)
with type
r6
What I’m actually trying to do is reimplement the automatic differentiation using Algebraic Effects with Extensible Effects, and not using the READER effect.
I have tried to fix this error but cannot find an easy way. Any suggestions on how to do this?
The following compiles, the Union constraint drops out from what expand expects. It feels a little redundant, but maybe it’s saying something important about r, I haven’t put much thought into it.
testHandler :: forall r t. Union r (READER Int + ()) (READER Int + r) => Run (READER String + r) ~> Run (READER Int + r)
testHandler = interpret (on _reader handler (send <<< expand'))
where
expand' :: VariantF r ~> VariantF (READER Int + r)
expand' = expand
handler :: Reader String ~> Run (READER Int + r)
handler = case _ of
Reader k -> do
x <- ask
pure $ k (show x)
But expandOne :: forall sym r1 r2 f . IsSymbol sym => Cons sym f r1 r2 => Variant r1 -> Variant r2 can’t be created?
Maybe there is something some problem with duplicate field names?
This signature on it’s own isn’t solvable without explicit type applications, but even if it was, it isn’t typesafe given duplicate record labels. Union has a bias where the left side always takes precedence. Meaning, any input rows remain at the top of the stack for a given label. Your expandOne pushes the output to the top of the stack. If I had Variant (foo :: Int), with your signature I could write expandOne @"foo" @String wat :: Variant (foo :: String, foo Int), which just unsafe coerces an Int to String. If you wanted to implement this with Cons, you would need an additional Lacks constraint, much like with Records.
Yeah, something like that would be possible. One could also add the Union constraint to the callback making it available to a call to expand, or additionally provide some other type equality.