Haskell 如何使用自定义应用程序类型代替IO?

Haskell 如何使用自定义应用程序类型代替IO?,haskell,monad-transformers,io-monad,Haskell,Monad Transformers,Io Monad,我想在程序中使用自定义应用程序类型代替IO,并将其与async库中的函数一起使用 具体地说,我热衷于将两个类型为App的计算传递给race\uu。由于race只接受IO类型的值,因此我用return包装了这些计算 当这种类型检查时,我可以看到两种计算都没有实际执行 这里有一个简单的例子说明了这个问题: {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUA

我想在程序中使用自定义应用程序类型代替
IO
,并将其与
async
库中的函数一起使用

具体地说,我热衷于将两个类型为
App
的计算传递给
race\uu
。由于
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
类型,它们允许使用与此类似的
co log
进行日志记录。这是我不想失去的东西。

您的计算没有得到执行,因为您实际上没有将它们传递给
race
。相反,您正在传递两个
IO
计算,结果返回您的
App
计算。但不是执行它们

为了让它们在
IO
中执行,请使用您已有的函数
runApp
。由于您需要向它传递一个环境,并且我假设您希望使用与
simpleApp
本身相同的环境,因此您可以使用
ask
Monawarder
上下文中获取它:

simpleApp :: App () = do
     liftIO $ putStrLn "About to spawn threads"
     env <- ask
     liftIO $ race_ (runApp env firstAsync) (runApp env secondAsync)
simpleApp::App()=do
liftIO$putStrLn“即将生成线程”
你可能会考虑使用“unLIFITiO”包提供诸如<代码> race> <代码>之类的函数,它们在任何一个单元格上都有一个<代码> MonadUnliftIO < /Calp>实例(基本上,它们处理所有的新类型的包装和解包)。code>ReaderT
over
IO
已经有这样的实例,您可以为您的新类型派生一个实例。