一元.net类型
本文关键字:类型 net 一元 | 更新日期: 2023-09-27 18:11:10
Eric Lippert在一系列的文章中概述了所谓的。net类型的"Monad模式",它有点像Monad,并为其中一些类型实现了返回和绑定。
作为一元类型的例子,他给出:
-
Nullable<T>
-
Func<T>
-
Lazy<T>
-
Task<T>
-
IEnumerable<T>
我有两个问题:
-
我得到
Nullable<T>
有点像Haskell中的Maybe
,绑定几个Maybe
动作代表一组可能在任何时候失败的操作。我知道列表单子(IEnumerable<T>
)代表非确定性。我甚至有点理解Func
作为monad (Reader
monad)的作用。Lazy<T>
和Task<T>
的一元语义是什么?约束它们是什么意思? -
谁有更多的类型的例子,在。net有点像单子?
嗯,Haskell默认有惰性,所以这在Haskell中不是很有指导意义,但我仍然可以展示如何将Task
s实现为单子。下面是在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
函数所做的就是派生一个线程来计算一个动作,返回一个future。我只是在它周围定义了一个小包装器,这样我就可以定义一个Monad
实例。
使用这个API,您可以轻松地定义自己的Task
,只需在运行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
符号组合Task
s,创建一个新的延迟任务准备运行:
test3 :: Task Int
test3 = do
n1 <- test1
n2 <- test2
return (n1 + n2)
运行fork test3
将生成Task
并返回一个future,你可以在任何时候调用它来要求结果,如果需要的话阻塞直到完成。
为了证明它是有效的,我将做两个简单的测试。首先,我将分叉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
$
一元绑定函数的类型为:
Moand m => m a -> (a -> m b) -> m b
因此,对于c#中的Task<T>
,您需要一个函数来接受Task<A>
提取值并将其传递给绑定函数。如果任务出错或被取消,复合任务应该传播错误或取消。
使用async:
这是相当简单的public static async Task<B> SelectMany<A, B>(this Task<A> task, Func<A, Task<B>> bindFunc)
{
var res = await task;
return await bindFunc(res);
}
对于Lazy<T>
,你应该从一个函数中创建一个延迟值,该函数接受另一个延迟计算的结果:
public static Lazy<B> SelectMany<A, B>(this Lazy<A> lazy, Func<A, Lazy<B>> bindFunc)
{
return new Lazy<B>(() => bindFunc(lazy.Value).Value);
}
我认为
return bindFunc(lazy.Value);
是无效的,因为它急切地求lazy
的值,所以你需要构造一个新的lazy,从创建的lazy中解包值