概括Corotine悬挂类型

时间:2016-02-13 23:16:50

标签: haskell types coroutine

我正在尝试解决问题并且有一个协程,它将从外部请求所有IO个动作。因此,我有自定义暂停类型IORequest。问题是,对于每种类型的返回类型,我需要向IORequest添加一个额外的构造函数。

以下是一个工作示例(需要mtlmonad-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}}

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同构。这遵循左侧延伸的定律(即,lanToComposedAdjointcomposedAdjointToLan见证同构)但可以直接书写:

actuallyJustIO_1 :: IORequest a -> IO a 
actuallyJustIO_1 = fmap runIdentity . lanToComposedAdjoint 

actuallyJustIO_2 :: IO a -> IORequest a 
actuallyJustIO_2 = composedAdjointToLan . fmap Identity 

这两个函数显然是彼此左右颠倒的,见证了IORequestIO之间的同构。