This seems bad:
{-# LANGUAGE GHC2021 #-}
import Bluefin.IO qualified as B
import Bluefin.State qualified as B
import Bluefin.System.IO qualified as B
import System.IO (IOMode(ReadMode))
-- ghci> example
-- *** Exception: /dev/null: hPutChar: illegal operation (handle is closed)
example :: IO ()
example = B.runEff $ \io -> do
r <- B.withFile io "/dev/null" ReadMode $ \h -> do
B.withEffToIO_ io $ \effToIO -> do
pure (effToIO (B.hPutChar h 'X'))
B.effIO io r
Effectful has a similar problem, although it actually detects that it's happening, which would lead to greater safety in situations where the handles themselves don't dynamically check whether they're still alive.
{-# LANGUAGE GHC2021 #-}
import Effectful
import Effectful.State.Static.Local
example :: IO ()
example = B.runEff $ \io -> do
r <- B.withFile io "/dev/null" ReadMode $ \h -> do
B.withEffToIO_ io $ \effToIO -> do
pure (effToIO (B.hPutChar h 'X'))
B.effIO io r
-- ghci> foo
-- *** Exception: version (2) /= storageVersion (0)
-- If you're attempting to run an unlifting function outside of the scope of effects it captures, have a look at UnliftingStrategy (SeqForkUnlift).
-- CallStack (from HasCallStack):
-- error, called at src/Effectful/Internal/Env.hs:438:5 in effectful-core-2.5.0.0-8f0f88000fb1b321565aecfd7dab29776c11a03e795a51044aef3b74f0775084:Effectful.Internal.Env
-- getLocation, called at src/Effectful/Internal/Env.hs:390:14 in effectful-core-2.5.0.0-8f0f88000fb1b321565aecfd7dab29776c11a03e795a51044aef3b74f0775084:Effectful.Internal.Env
-- getEnv, called at src/Effectful/Internal/Monad.hs:621:35 in effectful-core-2.5.0.0-8f0f88000fb1b321565aecfd7dab29776c11a03e795a51044aef3b74f0775084:Effectful.Internal.Monad
-- getStaticRep, called at src/Effectful/State/Static/Local.hs:91:14 in effectful-core-2.5.0.0-8f0f88000fb1b321565aecfd7dab29776c11a03e795a51044aef3b74f0775084:Effectful.State.Static.Local
-- get, called at /home/tom//test28.hs:23:22 in fake-package-0-inplace:Main
foo = runEff $ do
r <- evalState (0 :: Int) $ do
withRunInIO $ \runInIO -> do
pure (runInIO (get @Int))
liftIO r
I think ultimately we have to declare that the MonadUnliftIO instance/withEffToIO_ are unsafe :(
This seems bad:
{-# LANGUAGE GHC2021 #-} import Bluefin.IO qualified as B import Bluefin.State qualified as B import Bluefin.System.IO qualified as B import System.IO (IOMode(ReadMode)) -- ghci> example -- *** Exception: /dev/null: hPutChar: illegal operation (handle is closed) example :: IO () example = B.runEff $ \io -> do r <- B.withFile io "/dev/null" ReadMode $ \h -> do B.withEffToIO_ io $ \effToIO -> do pure (effToIO (B.hPutChar h 'X')) B.effIO io rEffectful has a similar problem, although it actually detects that it's happening, which would lead to greater safety in situations where the handles themselves don't dynamically check whether they're still alive.
{-# LANGUAGE GHC2021 #-} import Effectful import Effectful.State.Static.Local example :: IO () example = B.runEff $ \io -> do r <- B.withFile io "/dev/null" ReadMode $ \h -> do B.withEffToIO_ io $ \effToIO -> do pure (effToIO (B.hPutChar h 'X')) B.effIO io r -- ghci> foo -- *** Exception: version (2) /= storageVersion (0) -- If you're attempting to run an unlifting function outside of the scope of effects it captures, have a look at UnliftingStrategy (SeqForkUnlift). -- CallStack (from HasCallStack): -- error, called at src/Effectful/Internal/Env.hs:438:5 in effectful-core-2.5.0.0-8f0f88000fb1b321565aecfd7dab29776c11a03e795a51044aef3b74f0775084:Effectful.Internal.Env -- getLocation, called at src/Effectful/Internal/Env.hs:390:14 in effectful-core-2.5.0.0-8f0f88000fb1b321565aecfd7dab29776c11a03e795a51044aef3b74f0775084:Effectful.Internal.Env -- getEnv, called at src/Effectful/Internal/Monad.hs:621:35 in effectful-core-2.5.0.0-8f0f88000fb1b321565aecfd7dab29776c11a03e795a51044aef3b74f0775084:Effectful.Internal.Monad -- getStaticRep, called at src/Effectful/State/Static/Local.hs:91:14 in effectful-core-2.5.0.0-8f0f88000fb1b321565aecfd7dab29776c11a03e795a51044aef3b74f0775084:Effectful.State.Static.Local -- get, called at /home/tom//test28.hs:23:22 in fake-package-0-inplace:Main foo = runEff $ do r <- evalState (0 :: Int) $ do withRunInIO $ \runInIO -> do pure (runInIO (get @Int)) liftIO rI think ultimately we have to declare that the
MonadUnliftIOinstance/withEffToIO_are unsafe :(