为什么Monad接口不能在Java中声明? [英] Why can the Monad interface not be declared in Java?

查看:176
本文介绍了为什么Monad接口不能在Java中声明?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在你开始阅读之前:这个问题不是关于单子的理解,而是关于识别Java类型系统的局限性,它阻止了 Monad 接口的声明。




为了理解monads,我阅读这个由Eric Lippert回答一个关于单子的简单解释的问题。在那里,他还列出了可以在monad上执行的操作:



  1. 取一个未放大的类型的值,并将其变成一个放大类型的值。
  2. 有一种方法可以将非放大类型的操作转换为放大类型的操作,服从前面提到的功能组合规则

  3. 通常有一种方法可以将非放大类型从放大类型中取出。 (最后一点对monad来说不是必须的,但通常情况下,这种操作是存在的。)


在阅读了关于monads的更多信息之后,我将第一个操作标识为 return 函数,第二个操作标识为 bind 函数。我无法找到第三个操作的常用名称,因此我将它称为 unbox 函数。



为了更好地理解monad,我继续尝试在Java中声明一个通用的 Monad 接口。为此,我首先查看了上述三个函数的签名。对于Monad M ,它看起来像这样:

  return :: T1  - > M< T1> 
bind :: M< T1> - > (T 1→M T 2)→> M< T2>
unbox :: M< T1> - > T1

return 一个 M 的实例,所以它不属于 Monad 界面。相反,它将作为构造函数或工厂方法实现。



现在,我省略了 unbox 函数来自接口声明,因为它不是必需的。这个函数对于接口的不同实现有不同的实现。



因此, Monad 接口包含绑定函数。



让我们尝试声明接口:

  public interface Monad {
Monad bind();
}

有两个缺陷:


  • bind 函数应返回具体实现,但它只返回接口类型。这是一个问题,因为我们在具体的子类型上声明了unbox操作。我将其称为问题1


    在接口声明中使用具体类型



    这解决了问题1:如果我对monad的理解是正确的,那么 bind 函数总是返回一个与调用它的monad具有相同具体类型的新monad 。因此,如果我有一个名为 M Monad 接口的实现,那么 M.bind 会返回另一个 M ,但不是 Monad 。我可以使用泛型来实现它:

      public interface Monad< M extends Monad< M>> {
    M bind();
    }

    public class MonadImpl< M extends MonadImpl< M>>实现Monad< M> {
    @Override
    public M bind(){/ *做东西并返回一个M * /}
    }

    起初,这看起来可行,但至少有两个缺陷:


    • 只要实现类不提供它自己,而是另一个 Monad 接口的实现作为类型参数 M ,因为那么 bind 方法将返回错误的类型。例如,

        public class FaultyMonad< M extends MonadImpl< M>>实现Monad< M> {...} 

      将返回一个 MonadImpl 它应该返回一个 FaultyMonad 的实例。但是,我们可以在文档中指定此限制,并将此类实现视为程序员错误。

    • 第二个缺陷更难以解决。我将它称为问题2 :当我尝试实例化类 MonadImpl 时,我需要提供类型 M 。让我们试试这个:

        new MonadImpl< MonadImpl< MonadImpl< MonadImpl< MonadImpl< ...>>>>>()

      ,这必须无限地继续下去。这是另一个尝试:

        public static< M extends MonadImpl< M>> MonadImpl< M> create(){
      return new MonadImpl< M>();
      }

      尽管这似乎有效,但我们只是将问题解决了。以下是该函数对我的唯一用法:

        public void createAndUseMonad(){
      MonadImpl< > monad = create();
      //使用monad
      }

      基本归结为

        MonadImpl<?> monad = new MonadImpl<>(); 

      但这显然不是我们想要的。



    在自己的声明中使用一个带有移位类型参数的类型



    现在,让我们将函数参数添加到 bind 函数:如上所述, bind 函数的签名如下所示: T1 - > M< T2> 。在Java中,这是类型函数< T1,M< T2>> 。这是第一次使用参数声明接口的尝试:

      public interface Monad< T1,M extends Monad <?,? >> {
    M bind(函数< T1,M>函数);

    $ / code>

    我们必须添加类型 T1 作为接口声明的泛型类型参数,所以我们可以在函数签名中使用它。第一个 M T1 C>。要用 T2 替换它,我们必须添加 T2 本身作为泛型类型参数:

      public interface Monad 
    T2> {
    M bind(函数< T1,M>函数);
    }

    现在,我们遇到了另一个问题。我们为 Monad 接口添加了第三个类型参数,所以我们必须在它的用法中添加一个新的 。现在我们将忽略新的来调查现在的第一个。它是 M 类型的返回monad的 M 。让我们尝试通过将 M 重命名为 M1 来移除并引入另一个 M2

      public interface Monad 
    T2,M2扩展Monad< ?,?,?,?>> {
    M1绑定(函数< T1,M1>函数);
    }

    介绍另一个 T3 结果是:

    pre $ public interface Monad
    T2,M2扩展Monad
    T3> {
    M1绑定(函数< T1,M1>函数);
    }

    并引入另一个 M3 结果:

      public interface Monad  ; 
    T2,M2扩展Monad
    T3, ?,?,?,?,?,?>> {
    M1绑定(函数< T1,M1>函数);
    }

    我们看到,如果我们试图解决所有 。这是问题3



    总结一下






    1. 在抽象类型声明中使用具体类型。
    2. 实例化一个类型接收自己作为泛型类型参数。

    3. 声明一个在其声明中使用自身的类型,并带有移位的类型参数。

    现在的问题是:Java类型系统中缺少的功能是什么?由于有些语言可以与monad一起工作,所以这些语言必须以某种方式声明 Monad 类型。这些其他语言如何声明 Monad 类型?我无法找到关于此的信息。我只能找到关于具体monad声明的信息,比如 Maybe monad。



    我错过了什么吗?我能否正确解决Java类型系统中的其中一个问题?如果我无法用Java类型系统解决问题2,是否有理由说Java为什么不提供有关不可实例化类型声明的警告?




    如前所述,这个问题是关于理解monad的 not 。如果我对单子的理解是错误的,你可以给出一个暗示,但不要试图给出解释。如果我对monad的理解是错误的,那么所描述的问题仍然存在。



    这个问题也不是关于是否可以声明 Monad 接口。这个问题已经得到了Eric Lippert在他上面提到的SO-answer中的回答:事实并非如此。这个问题是关于什么是阻止我做这件事的限制。 Eric Lippert将此称为高级类型,但我无法摆脱困境。


    大多数OOP语言没有丰富的足够的类型系统直接表示monad模式本身;你需要一个支持类型比泛型更高的类型系统。所以我不会这么做。相反,我会实现代表每个monad的通用类型,并实现代表您需要的三种操作的方法:将值转换为放大的值,将放大的值转换为值,并将未放大的值的函数转换为函数放大的值。


    解决方案


    缺少的功能在Java类型系统中?这些其他语言如何声明Monad类型?


    好问题!


    Eric Lippert将此称为高级类型,但我无法摆脱困境。


    你并不孤单。但他们实际上并没有听起来那么疯狂。



    让我们通过观察Haskell如何声明monadtype来回答你的两个问题 - 你会看到为什么在一分钟内报价。我简化了一些;标准monad模式在Haskell中也有一些其他操作:

      class Monad m其中
    (>> =):: ma - > (a - > m b) - > m b
    return :: a - > ma

    男孩,看起来既简单又完全不透明,不是吗?

    在这里,让我简化一下。 Haskell允许你声明你自己的中缀操作符来绑定,但我们只是将它称为绑定:

      class Monad m其中
    bind :: ma - > (a - > m b) - > m b
    return :: a - > m a

    好的,现在至少我们可以看到那里有两个monad操作。这意味着什么?



    正如你注意到的那样,首先要让你的头脑变得更有魅力的类型。 (正如Brian指出的那样,我在原来的答案中简化了这个术语,同样很有趣,你的问题引起了Brian的注意!)

    是类型的,并且类可能是通用的。所以在Java中我们有 int IFrob List< IBar> ,它们都是类型。

    从这一点上,你可以放弃任何关于长颈鹿作为动物的一个子类的直觉,等等;我们不会需要这个。想想一个没有继承权的世界;它不会再进入这个讨论。



    Java中的类是什么?那么,想到一个类最简单的方法就是它是 ,这对于一组具有相同属性的值来说,这些值中的任何一个都可以在需要类的实例时使用。你有一个类 Point ,可以说,如果你有一个类型为 Point 的变量,你可以指定任何实例的给它。 Point 类在某种意义上只是描述所有 Point 实例的集合。类是比实例更高的类



    在Haskell中,还有泛型和非泛型类型。 Haskell中的类是不是类型。在Java中,一个类描述了一组;无论何时您需要该类的实例,都可以使用该类型的值。在Haskell中,一个类描述了一组类型。这是Java类型系统缺失的关键特性。在Haskell中,一个类比一个类比实例更高。 Java只有两个层次结构; Haskell有三个。在Haskell中,你可以表达这样的想法:任何时候我需要一个具有特定操作的类型,我可以使用这个类的成员。



    在这里指出我正在做一些过于简单化的事情。以Java为例,例如 List< int> List< String> 。这些是两个类型,但是Java认为它们是一个类,所以从某种意义上说Java也有比类型更高的类。但是,在Haskell中, 列表x 列表y 是类型,并且 list 是一种比一种类型更高的东西,它可以产生一种类型,所以事实上,更准确的说Java具有 3 级别,而Haskell具有 4 。关键点仍然是:Haskell有一个概念,描述了一种比Java更强大的类型的操作,我们将在下面详细介绍这一点。)



    那么这是怎么回事不同于接口?这听起来像Java中的接口 - 您需要一种具有特定操作的类型,您可以定义一个描述这些操作的接口。我们将看到Java接口缺少什么。



    现在我们可以开始理解这个Haskell:

      class Monad m其中

    那么,什么是单子?这是一堂课。什么是课堂?这是一组有共同特点的类型,这样,无论何时需要一个具有特定操作的类型,都可以使用 Monad 类型。

    假设我们有一个类是该类的成员;称它为 m 。为了使该类型成为类 Monad

      bind :: ma  - > (a  - > m b) - > m b 
    return :: a - > ma

    操作名称位于 :: ,签名显示在右侧。因此,要成为 Monad ,类型 m 必须有两个操作: bind return 。这些操作的签名是什么?让我们看看 return first。

      a  - > ma 

    ma 是Haskell for Java将是 M< A> 。也就是说,这意味着 m 是一个泛型类型, a 是一个类型, ma m a 参数化。



    x - >在Haskell中,y 是一个函数,其类型为 x 并返回类型 y 。它是函数< X,Y>



    放在一起,我们有 return 是一个函数,它接受一个类型为 a 的参数并返回一个类型为 ma 。或在Java中

      static< A> M< a取代;返回(A a); 

    bind 有点困难。我认为OP很好理解这个签名,但是对于不熟悉简洁Haskell语法的读者,请稍微扩展一下。

    在Haskell中,一个论据。如果你想要一个两个参数的函数,你可以创建一个函数,它接受一个参数并返回一个参数的另一个函数。所以如果你有

      a  - > b  - > c 

    然后你有什么?一个函数,它接受 a 并返回一个 b - > ç。因此,假设你想创建一个带两个数字并返回它们的和的函数。您将创建一个采用第一个数字的函数,并返回一个采用第二个数字并将其添加到第一个数字的函数。

    在Java中,你会说

      static< A, B,C>功能< B,C> F(A a)

    所以如果你想要一个C而你有A和B,那么你(b)

    可以说

      > 

    有意义吗?



    好的,所以

      bind :: ma  - > (a  - > m b) - > mb 

    实际上是一个需要两件事的函数: ma ,以及 a - > m b ,它返回一个 m b 。或者,在Java中,它是直接的:

      static< A,B>功能<功能< A,M>,M B>绑定(M 

    或者,在Java中更习惯用法:

      static< A,B> M< B个绑定(M< A>,函数< A,M< B>)

    了解Java为什么不能直接表示monad类型。它没有能力说我有一类具有这种模式的类型。



    现在,您可以制作所需的所有单子类型Java的。你不能做的事情就是创建一个接口来表示这种类型是monad类型的想法。你需要做的是这样的:

      typeinterface Monad< M> 
    {
    static< A> M< a取代;返回(A a);
    static< A,B> M< B个绑定(M A m,函数A,M B> f);
    }

    看看类型接口如何谈论泛型类型本身?一元类型是任何类型的 M ,它具有一个类型参数,而具有这两个静态方法。但是你不能在Java或C#类型的系统中这样做。 绑定当然可以是一个实例方法,它将 M< A> 作为 this this 。但是没有办法让 Return 任何东西都是静态的。 Java不允许(1)通过未构造的泛型类型来对接口进行参数化,以及(2)无法指定静态成员是接口契约的一部分。


    由于有些语言可以与monad一起工作,所以这些语言必须以某种方式声明Monad类型。


    好吧,你会这样想,但实际上不是。首先,任何具有足够类型系统的语言都可以定义一元类型;你可以在C#或Java中定义所有你想要的monadic类型,但你不能说在类型系统中它们都有什么共同之处。例如,你不能创建一个只能由一元类型参数化的泛型类。第二,你可以用其他方式将monad模式嵌入到语言中。 C#没有办法说这个类型与monad模式匹配,但C#具有内置在语言中的查询理解(LINQ)。查询理解适用于任何一元类型!这只是绑定操作必须被称为 SelectMany ,这有点奇怪。但是如果你看看 SelectMany 的签名,你会发现它只是 bind

      static IEnumerable< R> SelectMany< S,R>(
    IEnumerable< S>源,
    Func< S,IEnumerable< R>选择器)

    这就是序列monad < IEnumerable< T> ,但是在C#中,如果你写了

      from x in a from y in b select z 

    然后 a 的类型可以是任何 monadic类型,不只是 IEnumerable< T> 。需要的是 a M ,即 b M ,并且在monad模式之后有一个合适的 SelectMany 。所以这是另一种在语言中嵌入monad识别器的方式,不需要直接在类型系统中表示。



    (前一段实际上是过分简化的谎言;由于性能的原因,这个查询使用的绑定模式与标准monadic绑定略有不同概念上它识别monad模式;实际上细节略有不同,请在这里阅读它们 http://ericlippert.com/2013/04/02/monads-part-twelve/ 如果你有兴趣的话)。

    还有一些小点:$ b​​
    $ b


    我无法找到第三个操作的常用名称,所以我将其称为unbox函数。


    不错的选择;它通常被称为提取操作。一个 monad 不需要公开提取操作,但当然不知何故 bind 需要能够获取 A <为了调用函数< A,M< B>> c $ c>>,因此逻辑上通常存在某种提取操作。



    comonad - 从某种意义上说,反向单声道 - 需要提取操作被暴露; extract 本质上是 return 向后。一个comonad也需要一个扩展操作,它的类型是 bind 向后转。它有签名静态M< B>延伸(M a m,Func f)


    Before you start reading: This question is not about understanding monads, but it is about identifying the limitations of the Java type system which prevents the declaration of a Monad interface.


    In my effort to understand monads I read this SO-answer by Eric Lippert on a question which asks about a simple explanation of monads. There, he also lists the operations which can be executed on a monad:

    1. That there is a way to take a value of an unamplified type and turn it into a value of the amplified type.
    2. That there is a way to transform operations on the unamplified type into operations on the amplified type that obeys the rules of functional composition mentioned before
    3. That there is usually a way to get the unamplified type back out of the amplified type. (This last point isn't strictly necessary for a monad but it is frequently the case that such an operation exists.)

    After reading more about monads, I identified the first operation as the return function and the second operation as the bind function. I was not able to find a commonly used name for the third operation, so I will just call it the unbox function.

    To better understand monads, I went ahead and tried to declare a generic Monad interface in Java. For this, I first looked at the signatures of the three functions above. For the Monad M, it looks like this:

    return :: T1 -> M<T1>
    bind   :: M<T1> -> (T1 -> M<T2>) -> M<T2>
    unbox  :: M<T1> -> T1
    

    The return function is not executed on an instance of M, so it does not belong into the Monad interface. Instead, it will be implemented as a constructor or factory method.

    Also for now, I omit the unbox function from the interface declaration, since it is not required. There will be different implementations of this function for the different implementations of the interface.

    Thus, the Monad interface only contains the bind function.

    Let's try to declare the interface:

    public interface Monad {
        Monad bind();
    }
    

    There are two flaws:

    • The bind function should return the concrete implementation, however it does only return the interface type. This is a problem, since we have the unbox operations declared on the concrete subtypes. I will refer to this as problem 1.
    • The bind function should retrieve a function as a parameter. We will address this later.

    Using the concrete type in the interface declaration

    This addresses problem 1: If my understanding of monads is correct, then the bind function always returns a new monad of the same concrete type as the monad where it was called on. So, if I have an implementation of the Monad interface called M, then M.bind will return another M but not a Monad. I can implement this using generics:

    public interface Monad<M extends Monad<M>> {
        M bind();
    }
    
    public class MonadImpl<M extends MonadImpl<M>> implements Monad<M> {
        @Override
        public M bind() { /* do stuff and return an instance of M */ }
    }
    

    At first, this seems to work, however there are at least two flaws with this:

    • This breaks down as soon as an implementing class does not provide itself but another implementation of the Monad interface as the type parameter M, because then the bind method will return the wrong type. For example the

      public class FaultyMonad<M extends MonadImpl<M>> implements Monad<M> { ... }
      

      will return an instance of MonadImpl where it should return an instance of FaultyMonad. However, we can specify this restriction in the documentation and consider such an implementation as a programmer error.

    • The second flaw is more difficult to resolve. I will call it problem 2: When I try to instantiate the class MonadImpl I need to provide the type of M. Lets try this:

      new MonadImpl<MonadImpl<MonadImpl<MonadImpl<MonadImpl< ... >>>>>()
      

      To get a valid type declaration, this has to go on infinitely. Here is another attempt:

      public static <M extends MonadImpl<M>> MonadImpl<M> create() {
          return new MonadImpl<M>();
      }
      

      While this seems to work, we just defered the problem to the called. Here is the only usage of that function that works for me:

      public void createAndUseMonad() {
          MonadImpl<?> monad = create();
          // use monad
      }
      

      which essentially boils down to

      MonadImpl<?> monad = new MonadImpl<>();
      

      but this is clearly not what we want.

    Using a type in its own declaration with shifted type parameters

    Now, let's add the function parameter to the bind function: As described above, the signature of the bind function looks like this: T1 -> M<T2>. In Java, this is the type Function<T1, M<T2>>. Here is the first attempt to declare the interface with the parameter:

    public interface Monad<T1, M extends Monad<?, ?>> {
        M bind(Function<T1, M> function);
    }
    

    We have to add the type T1 as generic type parameter to the interface declaration, so we can use it in the function signature. The first ? is the T1 of the returned monad of type M. To replace it with T2, we have to add T2 itself as a generic type parameter:

    public interface Monad<T1, M extends Monad<T2, ?, ?>,
                           T2> {
        M bind(Function<T1, M> function);
    }
    

    Now, we get another problem. We added a third type parameter to the Monad interface, so we had to add a new ? to the usage of it. We will ignore the new ? for now to investigate the now first ?. It is the M of the returned monad of type M. Let's try to remove this ? by renaming M to M1 and by introducing another M2:

    public interface Monad<T1, M1 extends Monad<T2, M2, ?, ?>,
                           T2, M2 extends Monad< ?,  ?, ?, ?>> {
        M1 bind(Function<T1, M1> function);
    }
    

    Introducing another T3 results in:

    public interface Monad<T1, M1 extends Monad<T2, M2, T3, ?, ?>,
                           T2, M2 extends Monad<T3,  ?,  ?, ?, ?>,
                           T3> {
        M1 bind(Function<T1, M1> function);
    }
    

    and introducing another M3 results in:

    public interface Monad<T1, M1 extends Monad<T2, M2, T3, M3, ?, ?>,
                           T2, M2 extends Monad<T3, M3,  ?,  ?, ?, ?>,
                           T3, M3 extends Monad< ?,  ?,  ?,  ?, ?, ?>> {
        M1 bind(Function<T1, M1> function);
    }
    

    We see that this will go on forever if we try to resolve all ?. This is problem 3.

    Summing it all up

    We identified three problems:

    1. Using the concrete type in the declaration of the abstract type.
    2. Instantiating a type which receives itself as generic type parameter.
    3. Declaring a type which uses itself in its declaration with shifted type parameters.

    The question is: What is the feature that is missing in the Java type system? Since there are languages which work with monads, these languages have to somehow declare the Monad type. How do these other languages declare the Monad type? I was not able to find information about this. I only find information about the declaration of concrete monads, like the Maybe monad.

    Did I miss anything? Can I properly solve one of these problems with the Java type system? If I cannot solve problem 2 with the Java type system, is there a reason why Java does not warn me about the not instantiable type declaration?


    As already stated, this question is not about understanding monads. If my understanding of monads is wrong, you might give a hint about it, but don't attempt to give an explanation. If my understanding of monads is wrong the described problems remain.

    This question is also not about whether it is possible to declare the Monad interface in Java. This question already received an answer by Eric Lippert in his SO-answer linked above: It is not. This question is about what exactly is the limitation that prevents me from doing this. Eric Lippert refers to this as higher types, but I can't get my head around them.

    Most OOP languages do not have a rich enough type system to represent the monad pattern itself directly; you need a type system that supports types that are higher types than generic types. So I wouldn't try to do that. Rather, I would implement generic types that represent each monad, and implement methods that represent the three operations you need: turning a value into an amplified value, turning an amplified value into a value, and transforming a function on unamplified values into a function on amplified values.

    解决方案

    What is the feature that is missing in the Java type system? How do these other languages declare the Monad type?

    Good question!

    Eric Lippert refers to this as higher types, but I can't get my head around them.

    You are not alone. But they are actually not as crazy as they sound.

    Let's answer both of your questions by looking at how Haskell declares the monad "type" -- you'll see why the quotes in a minute. I have simplified it somewhat; the standard monad pattern also has a couple other operations in Haskell:

    class Monad m where
      (>>=) :: m a -> (a -> m b) -> m b
      return :: a -> m a
    

    Boy, that looks both incredibly simple and completely opaque at the same time, doesn't it?

    Here, let me simplify that a bit more. Haskell lets you declare your own infix operator for bind, but we'll just call it bind:

    class Monad m where
      bind :: m a -> (a -> m b) -> m b
      return :: a -> m a
    

    All right, now at least we can see that there are the two monad operations in there. What does the rest of this mean?

    The first thing to get your head around, as you note, is "higher kinded types". (As Brian points out, I somewhat simplified this jargon in my original answer. Also quite amusing that your question attracted the attention of Brian!)

    In Java, a "class" is a kind of "type", and a class may be generic. So in Java we've got int and IFrob and List<IBar> and they're all types.

    From this point on throw away any intuition you have about Giraffe being a class that is a subclass of Animal, and so on; we won't need that. Think about a world with no inheritance; it will not come into this discussion again.

    What are classes in Java? Well, the easiest way to think of a class is that it is a name for a set of values that have something in common, such that any one of those values can be used when an instance of the class is required. You have a class Point, lets say, and if you have a variable of type Point, you can assign any instance of Point to it. The Point class is in some sense just a way to describe the set of all Point instances. Classes are a thing that is higher than instances.

    In Haskell there are also generic and non-generic types. A class in Haskell is not a kind of type. In Java, a class describes a set of values; any time you need an instance of the class, you can use a value of that type. In Haskell a class describes a set of types. That is the key feature that the Java type system is missing. In Haskell a class is higher than a type, which is higher than an instance. Java only has two levels of hierarchy; Haskell has three. In Haskell you can express the idea "any time I need a type that has certain operations, I can use a member of this class".

    (ASIDE: I want to point out here that I am making a bit of an oversimplification . Consider in Java for example List<int> and List<String>. These are two "types", but Java considers them to be one "class", so in a sense Java also has classes which are "higher" than types. But then again, you could say the same in Haskell, that list x and list y are types, and that list is a thing that is higher than a type; it's a thing that can produce a type. So it would in fact be more accurate to say that Java has three levels, and Haskell has four. The point remains though: Haskell has a concept of describing the operations available on a type that is simply more powerful than Java has. We'll look at this in more detail below.)

    So how is this different than interfaces? This sounds like interfaces in Java -- you need a type that has certain operations, you define an interface that describes those operations. We'll see what is missing from Java interfaces.

    Now we can start making sense of this Haskell:

    class Monad m where
    

    So, what is Monad? It's a class. What is a class? It's a set of types that have something in common, such that whenever you need a type that has certain operations, you can use a Monad type.

    Suppose we have a type that is a member of this class; call it m. What are the operations that must be on this type in order for that type to be a member of the class Monad?

      bind :: m a -> (a -> m b) -> m b
      return :: a -> m a
    

    The name of the operation comes to the left of the ::, and the signature comes to the right. So to be a Monad, a type m must have two operations: bind and return. What are the signatures of those operations? Let's look at return first.

      a -> m a
    

    m a is Haskell for what in Java would be M<A>. That is, this means m is a generic type, a is a type, m a is m parametrized with a.

    x -> y in Haskell is the syntax for "a function which takes type x and returns type y". It's Function<X, Y>.

    Put it together, and we have return is a function that takes an argument of type a and returns a value of type m a. Or in Java

    static <A>  M<A> Return(A a);
    

    bind is a little bit harder. I think the OP well understands this signature, but for readers who are unfamiliar with the terse Haskell syntax, let me expand on this a bit.

    In Haskell, functions only take one argument. If you want a function of two arguments, you make a function that takes one argument and returns another function of one argument. So if you have

    a -> b -> c
    

    Then what have you got? A function that takes an a and returns a b -> c. So suppose you wanted to make a function that took two numbers and returned their sum. You would make a function that takes the first number, and returns a function that takes a second number and adds it to the first number.

    In Java you'd say

    static <A, B, C>  Function<B, C> F(A a)
    

    So if you wanted a C and you had and A and a B, you could say

    F(a)(b)
    

    Make sense?

    All right, so

      bind :: m a -> (a -> m b) -> m b
    

    is effectively a function that takes two things: an m a, and a a -> m b and it returns an m b. Or, in Java, it is directly:

    static <A, B> Function<Function<A, M<B>>, M<B>> Bind(M<A>)
    

    Or, more idiomatically in Java:

    static <A, B> M<B> Bind(M<A>, Function<A, M<B>>) 
    

    So now you see why Java cannot represent the monad type directly. It does not have the ability to say "I have a class of types that have this pattern in common".

    Now, you can make all the monadic types you want in Java. The thing you can't do is make an interface that represents the idea "this type is a monad type". What you would need to do is something like:

    typeinterface Monad<M>
    {
      static <A>    M<A> Return(A a);
      static <A, B> M<B> Bind(M<A> m, Function<A, M<B>> f);
    }
    

    See how the type interface talks about the generic type itself? A monadic type is any type M that is generic with one type parameter and has these two static methods. But you can't do that in the Java or C# type systems. Bind of course could be an instance method that takes an M<A> as this. But there is no way to make Return anything but static. Java gives you no ability to (1) parameterize an interface by an unconstructed generic type, and (2) no ability to specify that static members are part of the interface contract.

    Since there are languages which work with monads, these languages have to somehow declare the Monad type.

    Well you'd think so but actually not. First off, of course any language with a sufficient type system can define monadic types; you can define all the monadic types you want in C# or Java, you just can't say what they all have in common in the type system. You can't make a generic class that can only be parameterized by monadic types, for instance.

    Second, you can embed the monad pattern in the language in other ways. C# has no way to say "this type matches the monad pattern", but C# has query comprehensions (LINQ) built into the language. Query comprehensions work on any monadic type! It's just that the bind operation has to be called SelectMany, which is a little weird. But if you look at the signature of SelectMany, you'll see that it is just bind:

      static IEnumerable<R> SelectMany<S, R>(
        IEnumerable<S> source,
        Func<S, IEnumerable<R>> selector)
    

    That's the implementation of SelectMany for the sequence monad, IEnumerable<T>, but in C# if you write

    from x in a from y in b select z
    

    then a's type can be of any monadic type, not just IEnumerable<T>. What is required is that a is M<A>, that b is M<B>, and that there is a suitable SelectMany that follows the monad pattern. So that's another way of embedding a "monad recognizer" in the language, without representing it directly in the type system.

    (The previous paragraph is actually a lie of oversimplification; the binding pattern used by this query is slightly different than the standard monadic bind for performance reasons. Conceptually this recognizes the monad pattern; in actuality the details differ slightly. Read about them here http://ericlippert.com/2013/04/02/monads-part-twelve/ if you're interested.)

    A few more small points:

    I was not able to find a commonly used name for the third operation, so I will just call it the unbox function.

    Good choice; it is usually called the "extract" operation. A monad need not have an extract operation exposed, but of course somehow bind needs to be able to get the A out of the M<A> in order to call the Function<A, M<B>> on it, so logically some sort of extraction operation usually exists.

    A comonad -- a backwards monad, in a sense -- requires an extract operation to be exposed; extract is essentially return backwards. A comonad as well requires an extend operation that is sort of bind turned backwards. It has the signature static M<B> Extend(M<A> m, Func<M<A>, B> f)

    这篇关于为什么Monad接口不能在Java中声明?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆