【发布时间】:2020-02-01 14:58:09
【问题描述】:
我想在我的程序中使用自定义应用程序类型来代替IO,并将其与async 库中的race_ 等函数一起使用。
具体来说,我热衷于将App 类型的两个计算传递给race_。因为race_ 只接受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"
如何使用race_ 运行App 类型的这些计算?
¹ 虽然在此示例中删除 App 类型很简单,但在我正在构建的应用程序中,我有 App 和 Env 类型允许使用 @987654339 进行日志记录@类似于此setup。这是我不想失去的东西。
【问题讨论】:
-
你可以考虑使用“unliftio”包hackage.haskell.org/package/unliftio,它提供了像
race_hackage.haskell.org/package/unliftio-0.2.12/docs/…这样的函数,可以在任何带有MonadUnliftIO实例的monad上工作(基本上,它们处理所有的新类型包装并为你解包)。ReaderT超过IO已经有这样的实例,你可以为你的新类型派生一个。
标签: haskell monad-transformers io-monad