这是C#Monad,问题出在哪里? [英] Here is the C# Monad, where is the problem?
问题描述
阅读上一个SO问题我很困惑地发现 Eric Lippert 说无法在C#中定义接口对于所有Monad,请使用以下实现:
Reading a Previous SO Question I was confused to find Eric Lippert saying that an interface cannot be defined in C# for all Monads, using an implementation as below:
typeInterface Monad<MonadType<A>>
{
static MonadType<A> Return(A a);
static MonadType<B> Bind<B>(MonadType<A> x, Func<A, MonadType<B>> f);
}
我的问题是问题中列出的所有问题似乎都可以轻松解决:
My problem is all the problems listed in the question seem to have easy solutions:
- 没有更高种类的类型" =>使用父接口
- 接口中没有静态方法. =>为什么要使用静态?只需使用实例方法
Monad是一种模式,允许对包装类型的操作进行链接 为所有Monad定义一个C#接口似乎很容易,这使我们可以为所有Monad编写一个通用类 哪里出问题了?
Monad is a pattern allowing chaining of operations on wrapped types it seems easy to define a C# interface for all Monads allowing us to write a generic class for all monads Where's the problem?
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定义C#接口似乎很容易.哪里出问题了?
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 .)
I've omitted the "unwrap" because the existence of an extraction operation is not a requirement of a monad. (Many monads have this operation, but not all do. If you require an extract operation, you are probably actually using a comonad.)
您问为什么这是错误的.这在几种方面都是错误的.
You ask why this is wrong. This is wrong in several ways.
第一个错误的方法是:如果没有实例,就无法通过Wrap
创建monad的新实例!您在这里遇到了鸡和蛋的问题.
The first way it is wrong is: there is no way to create a new instance of the monad via Wrap
without already having an instance! You have a chicken-and-egg problem here.
包装"或单元"或返回"操作-无论您想称呼什么-在逻辑上都是静态工厂;这是如何创建monad的新实例.这不是对实例的操作.它是对类型的静态方法的要求. (或者,要求类型实现特定的构造函数,这实际上是同一件事.无论哪种方式,C#目前均不支持.)
The "wrap" or "unit" or "return" operation -- whatever you want to call it -- is logically a static factory; it's how you make a new instance of the monad. It's not an operation on an instance. It is a requirement of a static method on a type. (Or, the requirement that a type implement a particular constructor, which is effectively the same thing. Either way, it is not supported in C# at this time.)
让我们从下一点开始排除Wrap
.为什么Bind
错误?
Let's eliminate Wrap
from consideration in the next point. Why is Bind
wrong?
第二种错误的方法是您没有正确的限制.您的界面说,T的单子是提供绑定操作并返回U的单子的东西.但这还不够严格!假设我们有一个monad Maybe<T> : IMonad<T>
.现在假设我们有以下实现:
The second way it is wrong is you do not have the right restrictions in place. Your interface says that a monad of T is a thing that provides a bind operation that returns a monad of U. But that is not restrictive enough! Suppose we have a monad Maybe<T> : IMonad<T>
. Now suppose we have this implementation:
class Wrong<T> : IMonad<T>
{
public IMonad<U> Bind<U>(Func<T, IMonad<U>> map)
{
return new Maybe<U>();
}
}
满足合同,这告诉我们合同不是真正的单子合同. monad合同应该是Wrong<T>.Bind<U>
返回Wrong<U>
,而不是IMonad<U>
!但是我们无法用C#表示"bind返回定义了bind的类的实例".
That satisfies the contract, which tells us that the contract is not the real monad contract. The monad contract should be that Wrong<T>.Bind<U>
returns Wrong<U>
, not IMonad<U>
! But we have no way of expressing in C# "bind returns an instance of the class which defines bind".
类似地,这是错误的,因为必须要求调用者提供的Func
返回Wrong<U>
,而不是IMonad<U>
.假设我们有第三个monad,例如State<T>
.我们本来可以
Similarly it is wrong because the Func
that is provided by the caller must be required to return Wrong<U>
, not IMonad<U>
. Suppose we have a third monad, say, State<T>
. We could have
Wrong<Frog> w = whatever;
var result = w.Bind<Newspaper>(t=>new State<Newspaper>());
现在,这一切都搞砸了. Wrong<T>.Bind<U>
必须采用返回某些Wrong<U>
的函数,并且本身必须返回相同类型的Wrong<U>
,但是此接口允许我们具有一个绑定,该绑定采用一种返回State<Newspaper>
的函数,但该绑定返回Maybe<Newspaper>
.这完全违反了monad模式.您尚未在界面中捕获monad模式.
And now this is all messed up. Wrong<T>.Bind<U>
must take a function that returns some Wrong<U>
and must itself return Wrong<U>
of the same type, but this interface allows us to have a bind that takes a function that returns State<Newspaper>
but the bind returns Maybe<Newspaper>
. This is a total violation of the monad pattern. You have not captured the monad pattern in your interface.
C#类型系统不够强大,无法表达约束实现该方法时,它必须返回执行该实现的类的实例".如果C#具有"this_type"编译时注释,则Bind
可以表示为接口,但是C#没有该注释.
The C# type system is not strong enough to express the constraint "when the method is implemented it must return an instance of the class that did the implementation". If C# had a "this_type" compile-time annotation then Bind
could be expressed as an interface, but C# does not have that annotation.
这篇关于这是C#Monad,问题出在哪里?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!