阅读 Previous SO Question我很困惑地找到 Eric Lippert说不能在 C# 中为所有 Monad 定义接口(interface),使用如下实现:
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# 接口(interface)似乎很容易,允许我们为所有 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);
}
}
}
最佳答案
it seems easy to define a C# interface for all monads. Where's the problem?
你的提议是:
interface IMonad<T>
{
IMonad<T> Wrap(T a);
IMonad<U> Bind<U>(Func<T, IMonad<U>> map);
}
我省略了“展开”,因为提取操作的存在不是 monad 的要求。 (许多 monad 都有这个操作,但不是所有的都有。如果你需要一个提取操作,你可能实际上是在使用一个comonad。)
你问为什么这是错误的。这在几个方面是错误的。
错误的第一种方式是:无法通过
Wrap
创建 monad 的新实例。还没有实例!你有一个先有鸡还是先有蛋的问题。“wrap”、“unit”或“return”操作——不管你怎么称呼它——在逻辑上是一个静态工厂;这就是创建 monad 的新实例的方式。这不是对实例的操作。这是对类型的静态方法的要求。 (或者,要求一个类型实现一个特定的构造函数,这实际上是同一件事。无论哪种方式,目前 C# 都不支持它。)
让我们消除
Wrap
从下一点考虑。为什么是 Bind
错误的?第二种错误的方式是您没有适当的限制。您的界面说 T 的单子(monad)是提供返回 U 单子(monad)的绑定(bind)操作的东西。但这还不够严格!假设我们有一个单子(monad)
Maybe<T> : IMonad<T>
.现在假设我们有这个实现:class Wrong<T> : IMonad<T>
{
public IMonad<U> Bind<U>(Func<T, IMonad<U>> map)
{
return new Maybe<U>();
}
}
这满足了合约,它告诉我们合约不是真正的 monad 合约。 monad 合约应该是
Wrong<T>.Bind<U>
返回 Wrong<U>
,而不是 IMonad<U>
!但是我们无法在 C# 中表达“绑定(bind)返回定义绑定(bind)的类的实例”。同样是错误的,因为
Func
调用者提供的必须返回 Wrong<U>
,而不是 IMonad<U>
.假设我们有第三个单子(monad),比如说,State<T>
.我们可以有Wrong<Frog> w = whatever;
var result = w.Bind<Newspaper>(t=>new State<Newspaper>());
而现在这一切都搞砸了。
Wrong<T>.Bind<U>
必须采用返回一些 Wrong<U>
的函数并且必须自己返回 Wrong<U>
相同的类型,但是这个接口(interface)允许我们有一个绑定(bind),它接受一个返回 State<Newspaper>
的函数。但绑定(bind)返回 Maybe<Newspaper>
.这完全违反了单子(monad)模式。您尚未在界面中捕获 monad 模式。C# 类型系统不足以表达约束“当方法实现时,它必须返回执行实现的类的实例”。如果 C# 有一个“this_type”编译时注解,那么
Bind
可以表示为接口(interface),但 C# 没有该注释。
关于c# - 这是 C# Monad,问题出在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59921510/