我正在尝试解决问题并且有一个协程,它将从外部请求所有IO
个动作。因此,我有自定义暂停类型IORequest
。问题是,对于每种类型的返回类型,我需要向IORequest
添加一个额外的构造函数。
以下是一个工作示例(需要mtl和monad-coroutine)
{-# LANGUAGE DeriveFunctor #-}
module Main where
import Control.Monad.State
import Control.Monad.Coroutine
main :: IO ()
main = loop coroutine initialState
initialState :: Int
initialState = 65432
data IORequest x
= RunIO (IO ()) (() -> x)
| GetString (IO String) (String -> x)
deriving Functor
request :: Monad m => IO () -> Coroutine IORequest m ()
request x = suspend (RunIO x return)
requestString :: Monad m => IO String -> Coroutine IORequest m String
requestString x = suspend (GetString x return)
coroutine :: Coroutine IORequest (State Int) Int
coroutine = do
str <- requestString (readFile "my.txt")
request (print "hello")
return 5
loop :: Coroutine IORequest (State Int) Int -> Int -> IO ()
loop routine state =
do let (request, state') = runState (resume routine) state
case request of
Left (GetString cmd q') -> do
str <- cmd
loop (q' str) state'
Left (RunIO cmd q') -> do
cmd
loop (q' ()) state'
Right result -> do
print result
正如您所看到的那样,在某些时候我需要运行操作IO Bool
我需要扩展我的IORequest
并提供另一种帮助方法以便能够成功使用它(并且还扩展模式匹配loop
)。
问题:IORequest
可以某种方式推广以允许一般的(IO a) -> a
过渡吗?
像
这样的东西data IORequest x
= forall a. RunIO (IO a) (a -> x)
(我无法使其正常工作,因为a
会在str <- cmd
中尝试运行时失去{/ 1}}
答案 0 :(得分:1)
关于更改数据类型以包含存在量词的评论似乎非常合理。你的大部分功能都没有改变。
data IORequest x = forall a . RunIO (IO a) (a -> x)
instance Functor IORequest where
fmap f (RunIO x g) = RunIO x (f.g)
将其他构造函数替换为RunIO
:
request :: Monad m => IO () -> Coroutine IORequest m ()
request x = suspend (RunIO x return)
requestString :: Monad m => IO String -> Coroutine IORequest m String
requestString x = suspend (RunIO x return)
你的循环函数也没有真正改变 - 你只需要不忽略RunIO
内的值:
loop :: Coroutine IORequest (State Int) Int -> Int -> IO ()
loop routine state =
do let (request, state') = runState (resume routine) state
case request of
Left (RunIO cmd q') -> do
a <- cmd
loop (q' a) state'
Right result -> do
print result
请注意,您的IORequest
也可以这样定义(如果您有最近的GHC):
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
import Data.Functor.Kan.Lan (Lan(..))
import Data.Functor.Identity (Identity(..))
type IORequest = Lan Identity IO
pattern RunIO :: IO a -> (a -> x) -> IORequest x
pattern RunIO x f <- Lan ((.Identity) -> f) x
where RunIO x f = Lan (f.runIdentity) x
然后很明显IORequest
现在与IO
同构。这遵循左侧延伸的定律(即,lanToComposedAdjoint
和composedAdjointToLan
见证同构)但可以直接书写:
actuallyJustIO_1 :: IORequest a -> IO a
actuallyJustIO_1 = fmap runIdentity . lanToComposedAdjoint
actuallyJustIO_2 :: IO a -> IORequest a
actuallyJustIO_2 = composedAdjointToLan . fmap Identity
这两个函数显然是彼此左右颠倒的,见证了IORequest
和IO
之间的同构。