嗯,Haskell 默认有惰性,所以这在 Haskell 中不会很有启发性,但我仍然可以展示如何将 Tasks 实现为 monad。以下是在 Haskell 中实现它们的方法:
import Control.Concurrent.Async (async, wait)
newtype Task a = Task { fork :: IO (IO a) }
newTask :: IO a -> Task a
newTask io = Task $ do
w <- async io
return (wait w)
instance Monad Task where
return a = Task $ return (return a)
m >>= f = newTask $ do
aFut <- fork m
a <- aFut
bFut <- fork (f a)
bFut
为方便起见,它基于async 库构建,但并非必须如此。 async 函数所做的只是派生一个线程来评估一个动作,返回一个未来。我只是定义了一个小包装器,以便我可以定义一个Monad 实例。
使用此 API,您可以轻松定义自己的 Tasks,只需在 Task 运行时提供您想要分叉的操作:
import Control.Concurrent (threadDelay)
test1 :: Task Int
test1 = newTask $ do
threadDelay 1000000 -- Wait 1 second
putStrLn "Hello,"
return 1
test2 :: Task Int
test2 = newTask $ do
threadDelay 1000000
putStrLn " world!"
return 2
然后您可以使用do 表示法组合Tasks,这会创建一个准备运行的新延迟任务:
test3 :: Task Int
test3 = do
n1 <- test1
n2 <- test2
return (n1 + n2)
运行 fork test3 将生成 Task 并返回一个未来,您可以随时调用它来请求结果,如果需要,阻塞直到完成。
为了证明它有效,我将做两个简单的测试。首先,我将 fork test3 而不要求它的未来,只是为了确保它正确生成复合线程:
main = do
fork test3
getLine -- wait without demanding the future
这可以正常工作:
$ ./task
Hello,
world!
<Enter>
$
现在我们可以测试当我们要求结果时会发生什么:
main = do
fut <- fork test3
n <- fut -- block until 'test3' is done
print n
...这也有效:
$ ./task
Hello,
world!
3
$