这是 C# Monad,问题出在哪里?
Here is the C# Monad, where is the problem?
阅读 I was confused to find 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);
}
我的问题是问题中列出的所有问题似乎都有简单的解决方案:
- 否 "higher kinded types" => 使用父接口
- 接口中没有静态方法。 => 为什么要使用静态?!只使用实例方法
Monad 是一种允许对包装类型进行操作链接的模式
为所有 Monad 定义一个 C# 接口似乎很容易,允许我们为所有 Monad 编写一个通用的 class
问题出在哪里?
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);
}
我省略了 "unwrap" 因为提取操作的存在不是 monad 的要求。 (许多 monad 都有这个操作,但不是全部都有。如果你 需要 一个提取操作,你可能实际上正在使用 comonad。)
你问为什么这是错误的。这在几个方面是错误的。
错误的第一种方式是:没有实例就无法通过 Wrap
创建 monad 的新实例!你在这里遇到了先有鸡还是先有蛋的问题。
"wrap" 或 "unit" 或 "return" 操作——不管你怎么称呼它——在逻辑上是一个静态工厂; 如何创建 monad 的新实例。这不是对实例的操作。这是对类型的静态方法的要求。 (或者,类型实现特定构造函数的要求,这实际上是同一件事。无论哪种方式,目前 C# 都不支持。)
让我们在下一点的考虑中排除 Wrap
。为什么 Bind
错误?
第二种错误的方式是你没有适当的限制。您的界面说 T 的 monad 是提供绑定操作的东西 return 是 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>();
}
}
满足契约,这告诉我们契约不是真正的monad契约。 monad契约应该是Wrong<T>.Bind<U>
returns Wrong<U>
,而不是IMonad<U>
!但是我们在C#中没有办法表达"bind returns an instance of the class which defines bind".
同样是错误的,因为调用者提供的Func
必须是returnWrong<U>
,而不是IMonad<U>
。假设我们有第三个 monad,比如说 State<T>
。我们可以
Wrong<Frog> w = whatever;
var result = w.Bind<Newspaper>(t=>new State<Newspaper>());
现在这一切都搞砸了。 Wrong<T>.Bind<U>
必须采用 return 一些 Wrong<U>
的函数,并且它本身必须 return Wrong<U>
属于同一类型,但是这个接口允许我们有一个绑定采用 returns State<Newspaper>
但绑定 returns Maybe<Newspaper>
的函数。这完全违反了 monad 模式。您还没有在界面中捕获 monad 模式。
C# 类型系统不够强大,无法表达约束 "when the method is implemented it must return an instance of the class that did the implementation"。如果 C# 有一个 "this_type" 编译时注释,那么 Bind
可以表示为一个接口,但 C# 没有那个注释。
阅读
typeInterface Monad<MonadType<A>>
{
static MonadType<A> Return(A a);
static MonadType<B> Bind<B>(MonadType<A> x, Func<A, MonadType<B>> f);
}
我的问题是问题中列出的所有问题似乎都有简单的解决方案:
- 否 "higher kinded types" => 使用父接口
- 接口中没有静态方法。 => 为什么要使用静态?!只使用实例方法
Monad 是一种允许对包装类型进行操作链接的模式 为所有 Monad 定义一个 C# 接口似乎很容易,允许我们为所有 Monad 编写一个通用的 class 问题出在哪里?
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);
}
我省略了 "unwrap" 因为提取操作的存在不是 monad 的要求。 (许多 monad 都有这个操作,但不是全部都有。如果你 需要 一个提取操作,你可能实际上正在使用 comonad。)
你问为什么这是错误的。这在几个方面是错误的。
错误的第一种方式是:没有实例就无法通过 Wrap
创建 monad 的新实例!你在这里遇到了先有鸡还是先有蛋的问题。
"wrap" 或 "unit" 或 "return" 操作——不管你怎么称呼它——在逻辑上是一个静态工厂; 如何创建 monad 的新实例。这不是对实例的操作。这是对类型的静态方法的要求。 (或者,类型实现特定构造函数的要求,这实际上是同一件事。无论哪种方式,目前 C# 都不支持。)
让我们在下一点的考虑中排除 Wrap
。为什么 Bind
错误?
第二种错误的方式是你没有适当的限制。您的界面说 T 的 monad 是提供绑定操作的东西 return 是 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>();
}
}
满足契约,这告诉我们契约不是真正的monad契约。 monad契约应该是Wrong<T>.Bind<U>
returns Wrong<U>
,而不是IMonad<U>
!但是我们在C#中没有办法表达"bind returns an instance of the class which defines bind".
同样是错误的,因为调用者提供的Func
必须是returnWrong<U>
,而不是IMonad<U>
。假设我们有第三个 monad,比如说 State<T>
。我们可以
Wrong<Frog> w = whatever;
var result = w.Bind<Newspaper>(t=>new State<Newspaper>());
现在这一切都搞砸了。 Wrong<T>.Bind<U>
必须采用 return 一些 Wrong<U>
的函数,并且它本身必须 return Wrong<U>
属于同一类型,但是这个接口允许我们有一个绑定采用 returns State<Newspaper>
但绑定 returns Maybe<Newspaper>
的函数。这完全违反了 monad 模式。您还没有在界面中捕获 monad 模式。
C# 类型系统不够强大,无法表达约束 "when the method is implemented it must return an instance of the class that did the implementation"。如果 C# 有一个 "this_type" 编译时注释,那么 Bind
可以表示为一个接口,但 C# 没有那个注释。