gpt4 book ai didi

c# - Haskell 相当于 C# 5 async/await

转载 作者:可可西里 更新时间:2023-11-01 08:45:33 25 4
gpt4 key购买 nike

我刚刚阅读了有关使用 await 在 C# 5.0 中处理异步函数的新方法。和 async关键字。来自 C# reference on await 的示例:

private async Task SumPageSizesAsync()
{
// To use the HttpClient type in desktop apps, you must include a using directive and add a
// reference for the System.Net.Http namespace.
HttpClient client = new HttpClient();
// . . .
Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
byte[] urlContents = await getContentsTask;

// Equivalently, now that you see how it works, you can write the same thing in a single line.
//byte[] urlContents = await client.GetByteArrayAsync(url);
// . . .
}

A Task<byte[]>表示将生成 byte[] 类型值的异步任务的 Future .使用关键字 awaitTask 上基本上会将函数的其余部分放在任务完成时调用的延续中。任何使用 await 的函数必须使用关键字 async并输入 Task<a>如果它会返回类型 a .

所以线条

byte[] urlContents = await getContentsTask;
// Do something with urlContents

会翻译成类似的东西

Task newTask = getContentsTask.registerContinuation(
byte[] urlContents => {
// Do something with urlContents
});
return newTask;

这感觉很像 Monad(-transformer?)。感觉应该是与 CPS monad 有一些关系,但也可能不是。

这是我尝试编写相应的 Haskell 类型

-- The monad that async functions should run in
instance Monad Async
-- The same as the the C# keyword
await :: Async (Task a) -> Async a
-- Returns the current Task, should wrap what corresponds to
-- a async method in C#.
asyncFunction :: Async a -> Async (Task a)
-- Corresponds to the method Task.Run()
taskRun :: a -> Task a

以及上面例子的粗略翻译

instance MonadIO Async -- Needed for this example

sumPageSizesAsync :: Async (Task ())
sumPageSizesAsync = asyncFunction $ do
client <- liftIO newHttpClient
-- client :: HttpClient
-- ...
getContentsTask <- getByteArrayAsync client url
-- getContentsTask :: Task [byte]
urlContents <- await getContentsTask
-- urlContents :: [byte]

-- ...

这会是 Haskell 中的相应类型吗?是否有任何 Haskell 库以这种方式(或类似方式)实现处理异步函数/操作的方式?

另外:你能用 CPS 转换器构建这个吗?

编辑

是的, Control.Concurrent.Async module 确实解决了类似的问题(并且具有类似的界面),但是以完全不同的方式进行。我猜 Control.Monad.Task 将是一个更接近的比赛。我想(我认为)我正在寻找的是 Futures 的单子(monad)界面在幕后使用 Continuation Passing Style

最佳答案

这是一个构建在 async 库之上的 Task monad:

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

请注意,我还没有为此检查 monad 法则,因此它可能不正确。

这就是您定义在后台运行的原始任务的方式:

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 符号组合 Task,这会创建一个准备运行的新延迟任务:

test3 :: Task Int
test3 = do
n1 <- test1
n2 <- test2
return (n1 + n2)

运行 fork test3 将生成 Task 并返回一个 future ,您可以随时调用它来要求结果,必要时阻塞直到完成。

为了证明它有效,我将进行两个简单的测试。首先,我将 fork test3 而不要求它的 future ,只是为了确保它正确地生成复合线程:

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
$

关于c# - Haskell 相当于 C# 5 async/await,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20295604/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com