Skip to content

MonadUnliftIO instance allows escape #29

Description

@tomjaguarpaw

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 :(

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions