这是C# Monad,问题出在哪里?

2024-02-01

读一上一个问题 https://stackoverflow.com/questions/35951818/why-can-the-monad-interface-not-be-declared-in-java/35959910#35959910?newreg=ad78d85f7589403db182f4211ea4cf2b我很困惑地发现埃里克·利珀特 https://stackoverflow.com/users/88656/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 定义一个 C# 接口似乎很容易。问题出在哪里?

您的建议是:

interface IMonad<T>
{
    IMonad<T> Wrap(T a);
    IMonad<U> Bind<U>(Func<T, IMonad<U>> map);
}

我省略了“展开”,因为提取操作的存在不是 monad 的要求。 (许多单子都有这个操作,但不是全部都有。如果你require提取操作,您可能实际上正在使用comonad.)

你问为什么这是错误的。这在几个方面都是错误的。

第一种错误是:无法通过以下方式创建 monad 的新实例Wrap还没有实例!这里有一个先有鸡还是先有蛋的问题。

“wrap”或“unit”或“return”操作——无论你怎么称呼它——在逻辑上是一个静态工厂;它是如何创建 monad 的新实例。这不是对实例的操作。这是类型上静态方法的要求。 (或者,要求类型实现特定的构造函数,这实际上是同一件事。无论哪种方式,目前 C# 都不支持它。)

让我们消除Wrap从下一点考虑。为什么是Bind wrong?

第二种错误是你没有适当的限制。你的接口说 T 的 monad 是一个提供返回 U 的 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>, not IMonad<U>!但我们无法在 C# 中表达“bind 返回定义 bind 的类的实例”。

同样,这是错误的,因为Func必须要求调用者返回Wrong<U>, not IMonad<U>。假设我们有第三个单子,比如说,State<T>。我们可以有

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 模式。

C# 类型系统不够强大,无法表达“当实现该方法时,它必须返回执行该实现的类的实例”的约束。如果 C# 有一个“this_type”编译时注释,那么Bind可以表示为接口,但 C# 没有该注释。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

这是C# Monad,问题出在哪里? 的相关文章

随机推荐