
The error in the simplified program can be explained.
... which fails to compile unless one either uncomments the explicit type declaration for the let-bind, or else inlines the value:
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeFamilies #-} module Main (main) where import Control.Monad.IO.Unlift (MonadUnliftIO, withRunInIO) import Control.Concurrent.MVar (newMVar, withMVar) import Control.Monad.IO.Class (liftIO) import Control.Monad.Reader (MonadReader, ReaderT, runReaderT, asks)
data Env = Env { envLocker :: !Locker, envString :: String } type Locker = forall a. IO a -> IO a
runLocked :: (env ~ Env, MonadReader env m, MonadUnliftIO m) => forall a. m a -> m a runLocked action = asks envLocker >>= \locker -> withRunInIO $ \run -> locker $ run action
-- XXX: To compile, uncomment let-bind type, or else inline! mkEnv :: IO Env mkEnv = newMVar () >>= \lock -> let -- locker :: Locker locker = withMVar lock . const in go locker "Hello World!" where go :: Locker -> String -> IO Env go envLocker envString = Env{..}
main :: IO () main = mkEnv >>= runReaderT (runLocked $ asks envString >>= liftIO . putStrLn)
You enabled TypeFamilies extension, which subsumes MonoLocalBinds.
MonoLocalBinds
disables automatic let-generalization. Unless you attach type
annotation, the type of locker
is not (forall a. IO a -> IO a).
This is a pure guess, but I think your error in the actual code is
caused by ApplicativeDo.
The following code fails to compile but disabling ApplicativeDo solves
the problem.
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ApplicativeDo #-}
module Main where
import Control.Concurrent.MVar
type Locker = forall a. IO a -> IO a
main :: IO ()
main =
do lock1 <- newMVar ()
let locker1 :: Locker
locker1 = withMVar lock1 . const
lock2 <- newMVar ()
let locker2 :: Locker
locker2 = withMVar lock2 . const
f locker1 locker2
f :: Locker -> Locker -> IO ()
f _ _ = putStrLn "dummy"
I think this is ApplicativeDo-side bug, not type checking bug.
--
/* Koji Miyazato