-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
unsafeCoerce
derivable from Coroutine
+locally
+abort
#13
Comments
I suspect you can implement Edit: yup. data Interleave r :: Effect where
Interleave :: m () -> Interleave r m r
runInterleave :: IOE :< effs
=> IO a
-> Eff (Interleave a ': effs) b -> Eff effs b
runInterleave io = handle pure (\(Interleave m) -> locally m >> liftH (liftIO io))
effUnsafeInterleaveIO :: forall a. IO a -> IO a
effUnsafeInterleaveIO io = do
stat <- runIO
$ runInterleave io
$ lift
$ runCoroutine
$ app' @a
return $ run $ interpret (\(Interleave _) -> undefined) $ case stat of
Yielded () c -> runCoroutine (c ()) >>= \case
Done a -> return a
app' :: Eff '[Coroutine () (), Interleave r] r
app' = send $ Interleave (yield ()) > a <- effUnsafeInterleaveIO (putStrLn "interleaved")
> a `seq` pure ()
interleaved
> |
I think this issue is fundamental for effect systems with the following properties:
This should be enough to do the dance in the OP that allows you to smuggle a computation that relies on a handler scope effect to outside of that scope, at which point you can't have any reasonable semantics (since doing this circumvents type-safety). So at least one of these has to go. Perhaps the most attractive target is the last property. locally :: Eff effs' a -> Handle localState eff effs i r effs' (localState a) But then you've probably reimplemented The easiest solution is probably to get rid of coroutines, but that means discarding or heavily restricting the Another option is to become first-order (discard Perhaps there are some other solutions than the ones I listed above, but I can't imagine one which wouldn't require a big change to |
This is obviously a bug (and a rather bad one), but I have a different diagnosis, probably because I’m thinking in terms of continuations. The issue is that The conceptual idea behind I like the following simpler example to illustrate why this is so hopeless. Consider a handler like this: runSomeAction :: Eff (SomeAction ': effs) a -> Eff effs a
runSomeAction = handle pure \case
SomeAction m -> locally m >> locally m Here we execute an action “right where we were,” then do it again after it returns. But what does the second use of In other words, we want
to reduce to something like
so that once we finish the first evaluation of
|
As I was studying @lexi-lambda's comment here, I realized that disappearing handlers through coroutines could be unsafe as-is. After some experimentation, turns out I was right:
This, unsurprisingly, still works if you use
Error
instead ofabort
directly.runCoroutine
internally usescontrol0
, so the culprit here is a nefarious interaction betweencontrol0
+locally
+abort
.The text was updated successfully, but these errors were encountered: