如何使用自定义 App 类型代替 IO?
How to use custom App type in place of IO?
我想在我的程序中使用自定义应用程序类型代替 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
类型允许使用类似于此 setup 的 co-log
进行日志记录。那是我不想失去的东西。
您的计算没有被执行,因为您实际上并没有将它们传递给 race_
。相反,您传递了两个 IO
计算,结果是 return 您的 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)
我想在我的程序中使用自定义应用程序类型代替 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
类型允许使用类似于此 setup 的 co-log
进行日志记录。那是我不想失去的东西。
您的计算没有被执行,因为您实际上并没有将它们传递给 race_
。相反,您传递了两个 IO
计算,结果是 return 您的 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)