我想在适当位置使用自定义的应用程序类型的IO
在我的计划,并与像函数使用它race_
从async
图书馆。
具体来说,我很想将type的两个计算传递App
给race_
。由于race_
只接受type的值IO
,因此我将这些计算打包为return
。
在进行这种类型检查时,我可以看到实际上没有执行任何计算。
这是说明问题的最小示例¹:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
module RaceTest where
import Control.Monad.Reader ( MonadReader
, ReaderT(..)
, runReaderT
)
import Control.Monad.IO.Class ( MonadIO
, liftIO
)
import Control.Concurrent.Async ( race_ )
data Env = Env { val :: !Int }
newtype App a = App
{ unApp :: ReaderT Env IO a
} deriving (Functor, Applicative, Monad, MonadIO, MonadReader Env)
runApp :: Env -> App a -> IO a
runApp env app = runReaderT (unApp app) env
main = runApp (Env 24) simpleApp
where
simpleApp :: App () = do
liftIO $ putStrLn "About to spawn threads"
liftIO $ race_ (return firstAsync) (return secondAsync)
firstAsync :: App () = liftIO $ putStrLn "First async"
secondAsync :: App () = liftIO $ putStrLn "Second async"
我怎样才能运行型的这些计算App
使用race_
?
¹ 尽管在本例中,简单地摆脱App
类型很简单,但是在我正在构建的应用程序中,我拥有App
和Env
类型,这些类型允许使用co-log
类似于此设置的日志记录。那是我不想失去的东西。
您的计算没有得到执行,因为您实际上没有将它们传递给race_
。相反,您要传递两个IO
计算,这些计算将返回App
结果。但是不执行它们。
为了使它们在内部执行IO
,请使用您runApp
已有的函数。由于您需要向其传递环境,并且假设您要使用与其simpleApp
本身相同的环境,因此可以ask
从MonadReader
上下文中获取它:
simpleApp :: App () = do
liftIO $ putStrLn "About to spawn threads"
env <- ask
liftIO $ race_ (runApp env firstAsync) (runApp env secondAsync)