Я бы хотел использовать пользовательский тип приложения вместо IO
в моей программе и использовать его с такими функциями, как race_
из библиотеки async
.
В частности, я заинтересован в передаче двух вычислений типа 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"
Как я могу выполнить эти вычисления типа App
, используя race_
?
¹ Хотя это в этом примере было бы просто избавиться от типа App
, в создаваемом приложении у меня есть типы App
и Env
, которые позволяют регистрировать с использованием co-log
, аналогично этой setup . Это то, что я не хочу терять.