【发布时间】:2020-01-26 18:39:54
【问题描述】:
阅读Previous SO Question 我很困惑地发现Eric Lippert 说不能在C# 中为所有Monad 定义接口,使用如下实现:
typeInterface Monad<MonadType<A>>
{
static MonadType<A> Return(A a);
static MonadType<B> Bind<B>(MonadType<A> x, Func<A, MonadType<B>> f);
}
我的问题是问题中列出的所有问题似乎都有简单的解决方案:
- 没有“高级类型” => 使用父接口
- 接口中没有静态方法。 => 为什么要使用静态?!只使用实例方法
Monad 是一种允许在包装类型上链接操作的模式 为所有 Monad 定义 C# 接口似乎很容易,允许我们为所有 monad 编写泛型类 问题出在哪里?
using System;
using System.Linq;
public class Program
{
public static void Main()
{//it works, where's the problem?
new SequenceMonad<int>(5)
.Bind(x => new SequenceMonad<float>(x + 7F))
.Bind(x => new SequenceMonad<double>(x + 5D))
;
}
interface IMonad<T>{
IMonad<T> Wrap(T a);
IMonad<U> Bind<U>(Func<T, IMonad<U>> map);
T UnWrap();//if we can wrap we should be able to unwrap
}
class GenericClassForAllMonads<T>
{//example writing logic for all monads
IMonad<U> DoStuff<U>(IMonad<T> input, Func<T, IMonad<U>> map)
{ return map(input.UnWrap()); }
}
class SequenceMonad<T> : IMonad<T> where T:new()
{//specific monad implementation
readonly T[] items;//immutable
public SequenceMonad(T a)
{
Console.WriteLine("wrapped:"+a);
items = new[] { a };
}
public IMonad<B> Bind<B>(Func<T, IMonad<B>> map)
{ return map(UnWrap()); }
public T UnWrap()
{ return items == null? default(T) : items.FirstOrDefault(); }
public IMonad<T> Wrap(T a)
{
Console.WriteLine("wrapped:"+a);
return new SequenceMonad<T>(a);
}
}
}
【问题讨论】:
-
所以为了在 Monad 中包装一个值,你首先必须创建一个?这真的有意义吗?
-
现在的问题是接口不能保证类有一个构造函数会包装这个值。 Eric 从未说过你不能用 C# 编写 monad,只是说你不能创建一个接口来保证实现它的类满足要求。
-
更准确地说,要求是传入一个值,然后得到一个包装该值的 monad。这可以是构造函数或静态方法,但都不能在接口中定义。剩下的就是实例方法,它们要么创建一个新的 monad,要么改变一个现有的 monad。但是要理解,从功能意义上讲,它接受一个 monad 和一个值并返回一个包装该值的 monad(但要求只传入该值)。
-
“如果我们可以包装,那么我们应该能够打开”不是单子法则。 “我们可以展开”是 comonad 法则,而“经常有展开”是关于常用 monad 的事实,而不是要求。
-
我注意到你的序列单子没有实现序列单子。序列单子的特征行为是绑定操作的行为是映射和连接的。也就是说,如果我们有一个整数序列
s等于{1, 2, 3},那么s.Bind(x => Repeat(x, 2))就是序列{1,1,2,2,3,3},例如。您的实现中没有任何串联逻辑。
标签: c# generics interface monads