ano*_*ous 5 c# generics monads interface
阅读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);
}
Run Code Online (Sandbox Code Playgroud)
我的问题是问题中列出的所有问题似乎都有简单的解决方案:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
Eri*_*ert 10
为所有 monad 定义 C# 接口似乎很容易。问题出在哪里?
你的提议是:
interface IMonad<T>
{
IMonad<T> Wrap(T a);
IMonad<U> Bind<U>(Func<T, IMonad<U>> map);
}
Run Code Online (Sandbox Code Playgroud)
我省略了“解包”,因为提取操作的存在不是 monad 的要求。(许多 monad 都有这个操作,但不是全部都有。如果你需要一个提取操作,你可能实际上正在使用comonad。)
你问为什么这是错误的。这在几个方面都是错误的。
第一个错误的方式是:如果没有实例,就无法通过 via 创建新的 monadWrap实例!你在这里有一个鸡和蛋的问题。
“wrap”或“unit”或“return”操作——不管你想怎么称呼它——在逻辑上是一个静态工厂;这就是你如何创建 monad 的新实例。这不是对实例的操作。这是对类型的静态方法的要求。(或者,要求类型实现特定的构造函数,这实际上是同一件事。无论哪种方式,目前 C# 都不支持它。)
让我们Wrap在下一点中排除考虑。为什么是Bind错的?
第二种错误方式是您没有适当的限制。您的接口说 T 的 monad 是一种提供绑定操作的东西,该操作返回 U 的 monad。但这还不够严格!假设我们有一个 monad Maybe<T> : IMonad<T>。现在假设我们有这个实现:
class Wrong<T> : IMonad<T>
{
public IMonad<U> Bind<U>(Func<T, IMonad<U>> map)
{
return new Maybe<U>();
}
}
Run Code Online (Sandbox Code Playgroud)
这满足合约,它告诉我们合约不是真正的 monad 合约。monad 契约应该是Wrong<T>.Bind<U>返回Wrong<U>,而不是IMonad<U>!但是我们无法在 C# 中表达“绑定返回定义绑定的类的实例”。
同样它是错误的,因为Func调用者提供的 必须要求返回Wrong<U>,而不是IMonad<U>。假设我们有第三个 monad,比如说,State<T>。我们可以有
Wrong<Frog> w = whatever;
var result = w.Bind<Newspaper>(t=>new State<Newspaper>());
Run Code Online (Sandbox Code Playgroud)
而现在这一切都一团糟。 Wrong<T>.Bind<U>必须接受一个返回 some 的函数,Wrong<U>并且必须本身返回Wrong<U>相同类型,但是这个接口允许我们有一个绑定,它接受一个返回State<Newspaper>但绑定返回的函数Maybe<Newspaper>。这完全违反了 monad 模式。您尚未在界面中捕获 monad 模式。
C# 类型系统不够强大,无法表达“在实现方法时,它必须返回执行该实现的类的实例”的约束。如果 C# 有“this_type”编译时注释,则Bind可以表示为接口,但 C# 没有该注释。