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

标签 c# generics interface monads

阅读 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);
}

我的问题是问题中列出的所有问题似乎都有简单的解决方案:
  • 没有“高级类型” => 使用父接口(interface)
  • 接口(interface)中没有静态方法。 => 为什么要使用静态?!只使用实例方法

  • 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/

    相关文章:

    c# - 垂直测量一根绳子

    java - Java 中的伪重载

    c# - 为什么我应该返回 IList<T> 而不是 List<T>?

    c# - 如何在 Entity Framework 中用 Enum 替换 Int 属性?

    c# - 取字符串的最后n行c#

    c# - 运算符和泛型类

    c# - 为什么仅在实现接口(interface)后才重写方法?

    java - 如何通过示例使用 Java 创建我自己的自定义界面

    c# - 接口(interface)还是抽象类?

    c# - 用于快速将 100K+ 行导出到 Excel 的 OpenXML Sax 方法