使用Java 8的Monads

Nim*_*sky 75 java monads optional java-8

为了帮助理解monad是什么,有人可以使用java提供一个例子吗?他们有可能吗?

如果你从这里下载预发布的lambda兼容JDK8,可以使用java表达lambda表达式http://jdk8.java.net/lambda/

使用此JDK的lambda示例如下所示,有人可以提供相对简单的monad吗?

public interface TransformService {
        int[] transform(List<Integer> inputs);
    }
    public static void main(String ars[]) {
        TransformService transformService = (inputs) -> {
            int[] ints = new int[inputs.size()];
            int i = 0;
            for (Integer element : inputs) {
                ints[i] = element;
            }
            return ints;
        };

        List<Integer> inputs = new ArrayList<Integer>(5) {{
            add(10);
            add(10);
        }};
        int[] results = transformService.transform(inputs);
    }
Run Code Online (Sandbox Code Playgroud)

ms-*_*-tg 75

仅供参考:

建议的JDK8 Optional确实满足三个Monad定律.这是一个证明这一点的要点.

Monad所需要的是提供符合三个定律的两个功能.

这两个功能:

  1. 值放入monadic上下文中

    • Haskell的可能:return/Just
    • Scala的选择: Some
    • 功能Java的选项: Option.some
    • JDK8的可选: Optional.of
  2. 在monadic上下文中应用函数

    • Haskell的可能:( >>=又名bind)
    • Scala的选择: flatMap
    • 功能Java的选项: flatMap
    • JDK8的可选: flatMap

请参阅上面的要点,了解这三个定律的java演示.

注意:要理解的关键事项之一是要在monadic上下文中应用函数的签名:它采用原始值类型,并返回monadic类型.

换句话说,如果你有一个实例Optional<Integer>,你可以传递给它的flatMap方法的函数将具有签名(Integer) -> Optional<U>,其中U是一个不必的值类型Integer,例如String:

Optional<Integer> maybeInteger = Optional.of(1);

// Function that takes Integer and returns Optional<Integer>
Optional<Integer> maybePlusOne = maybeInteger.flatMap(n -> Optional.of(n + 1));

// Function that takes Integer and returns Optional<String>
Optional<String> maybeString = maybePlusOne.flatMap(n -> Optional.of(n.toString));
Run Code Online (Sandbox Code Playgroud)

您不需要任何类型的Monad接口来以这种方式编码,或者以这种方式思考.在Scala中,您不编码为Monad接口(除非您使用的是Scalaz库...).似乎JDK8也将使Java人员能够使用这种链式monadic计算方式.

希望这有用!

更新:博客上讲述这个在这里.

  • Java的"可选"并不是一个真正的单子,因为使用生成"null"可以违反monad法则:https://developer.atlassian.com/blog/2015/08/optional-broken/ (2认同)
  • @Blaisorblade 这不是你的参考资料所说的。`Optional` 是一个 monad,以 `flatMap` 作为绑定操作符。 (2认同)

Fai*_*aiz 55

Java 8将有lambdas; monads是一个完全不同的故事.它们很难在函数式编程中解释(正如Haskell和Scala中关于该主题的大量教程所证明的那样).

Monads是静态类型函数式语言的典型特征.要用OO说话来描述它们,你可以想象一个Monad界面.实现的类Monad将被称为"monadic",前提是在实现Monad实现时遵循所谓的"monad法则".然后,该语言提供了一些语法糖,使得处理Monad类的实例变得有趣.

现在Iterable在Java中与monad无关,但作为Java编译器特别处理的类型的示例(foreachJava 5附带的语法),请考虑以下事项:

Iterable<Something> things = getThings(..);
for (Something s: things) {  /* do something with s */ }
Run Code Online (Sandbox Code Playgroud)

因此,虽然我们可以使用IterableIterator方法(hasNext在旧风格和公司)for环路,爪哇赐予我们这句法糖作为特例.

因此,正如实现Iterable并且Iterator必须遵守Iterator法律的类(示例:hasNext必须返回,false如果没有下一个元素)在foreach 语法中有用- 将存在几个对相应的符号有用的monadicdo(因为它在Haskell中调用) )或Scala的for表示法.

所以 -

  1. monadic类的好例子是什么?
  2. 处理它们的语法糖会是什么样的?

在Java 8中,我不知道 - 我知道lambda符号,但我不知道其他特殊的语法糖,所以我将不得不用另一种语言给你一个例子.

Monads通常用作容器类(列表就是一个例子).Java已经有了java.util.List明显不是monadic,但这里是Scala的:

val nums = List(1, 2, 3, 4)
val strs = List("hello", "hola")
val result = for { // Iterate both lists, return a resulting list that contains 
                   // pairs of (Int, String) s.t the string size is same as the num.
  n <- nums        
  s <- strs if n == s.length 
} yield (n, s)
// result will be List((4, "hola")) 
// A list of exactly one element, the pair (4, "hola")
Run Code Online (Sandbox Code Playgroud)

这是(大致)语法糖:

val nums = List(1, 2, 3, 4)
val strs = List("hello", "hola")
val results = 
nums.flatMap( n =>                 
  strs.filter(s => s.size == n).   // same as the 'if'
       map(s => (n, s))            // Same as the 'yield'
)
// flatMap takes a lambda as an argument, as do filter and map
// 
Run Code Online (Sandbox Code Playgroud)

这显示了Scala的一个功能,其中monad被利用来提供列表推导.

因此List,Scala是一个monad,因为它遵守Scala的monad定律,规定所有monad实现必须符合flatMap,map并且filter方法(如果你对法律感兴趣,"Monads is Elephants"博客条目有最好的描述我'到目前为止已发现).并且,正如您所看到的,lambdas(和HoF)是绝对必要的,不足以使这种事物以实用的方式有用.

除了容器之外,还有许多有用的monad.他们有各种各样的应用程序.我最喜欢的是OptionScala中的Maybemonad(Haskell中的monad),这是一个带来null安全性的包装类型:Optionmonad 的Scala API页面有一个非常简单的示例用法:http://www.scala-lang. org/api/current/scala/Option.html 在Haskell中,monad在表示IO时很有用,可以解决非monadic Haskell代码具有不确定执行顺序这一事实.

拥有lambdas是进入函数式编程世界的第一小步; monads需要monad约定和足够大的可用monadic类型,以及语法糖,以使它们有趣和有用.

由于Scala可以说是最接近Java的语言,它也允许(monadic)函数式编程,如果您(仍然)感兴趣的话,请查看Scala的Monad教程:http: //james-iry.blogspot.jp/2007/09/单子-都-大象-部分- 1.HTML

粗略的谷歌搜索显示至少有一次尝试在Java中执行此操作:https://github.com/RichardWarburton/Monads-in-Java -

遗憾的是,解释Java中的monad(即使使用lambdas)与解释ANSI C(而不是C++或Java)中的完整面向对象编程一样困难.

  • 好极了!_(听说你可能会尝试Scala)_ - 你会发现你可以完成大量的工作,超级高效,所有这些都没有在Scala中积极学习monad - 然后有一天你会突然发现你已经非常了解monad了! (4认同)
  • 我不同意你的第一个陈述.我不认为Haskell和Scala上monad的大量教程表明它们很难解释.相反,我认为它们的存在是为了解释权力单子所带来的巨大差异.关于monad的困难之处在于它虽然非常直观,但很难定义.因此,通过实例说明是最有效的. (2认同)

Mar*_*lli 10

即使monad可以用Java实现,任何涉及它们的计算都注定要成为泛型和大括号的混乱组合.

我会说Java绝对不是用来说明其工作或研究其含义和本质的语言.为此目的,使用JavaScript或支付一些额外的价格并学习Haskell要好得多.

无论如何,我发信号通知你,我刚刚使用新的Java 8 lambdas实现了一个状态monad.它绝对是一个宠物项目,但它适用于一个非平凡的测试用例.

你可能会在我的博客上找到它,但我会在这里给你一些细节.

状态monad基本上是从状态到对(状态,内容)的函数.您通常为状态赋予通用类型S,并将内容赋予通用类型A.

因为Java没有对,我们必须使用特定的类对它们进行建模,让我们称之为Scp(状态 - 内容对),在这种情况下它将具有泛型类型Scp<S,A>和构造函数new Scp<S,A>(S state,A content).在这之后我们可以说monadic函数将具有类型

java.util.function.Function<S,Scp<S,A>>
Run Code Online (Sandbox Code Playgroud)

这是一个@FunctionalInterface.也就是说,可以在不命名的情况下调用其唯一的实现方法,传递具有正确类型的lambda表达式.

该类StateMonad<S,A>主要是函数的包装器.它的构造函数可以用例如

new StateMonad<Integer, String>(n -> new Scp<Integer, String>(n + 1, "value"));
Run Code Online (Sandbox Code Playgroud)

状态monad将该函数存储为实例变量.然后有必要提供一种公共方法来访问它并将其提供给状态.我决定称之为s2scp("状态到状态 - 内容对").

要完成monad的定义,您必须提供一个单元(aka return)和一个bind(aka flatMap)方法.我个人更喜欢将unit指定为static,而bind是实例成员.

在州monad的情况下,单位必须如下:

public static <S, A> StateMonad<S, A> unit(A a) {
    return new StateMonad<S, A>((S s) -> new Scp<S, A>(s, a));
}
Run Code Online (Sandbox Code Playgroud)

而bind(作为实例成员)是:

public <B> StateMonad<S, B> bind(final Function<A, StateMonad<S, B>> famb) {
    return new StateMonad<S, B>((S s) -> {
        Scp<S, A> currentPair = this.s2scp(s);
        return famb(currentPair.content).s2scp(currentPair.state);
    });
}
Run Code Online (Sandbox Code Playgroud)

您注意到绑定必须引入泛型类型B,因为它是允许链接异构状态monad的机制,并为此和任何其他monad提供了将计算从一个类型移动到另一个类型的卓越功能.

我将停止使用Java代码.复杂的东西在GitHub项目中.与以前的Java版本相比,lambdas删除了很多大括号,但语法仍然相当复杂.

另外,我正在展示如何在其他主流语言中编写类似的状态monad代码.在Scala的情况下,bind(在这种情况下必须称为flatMap)读起来像

def flatMap[A, B](famb: A => State[S, B]) = new State[S, B]((s: S) => {
  val (ss: S, aa: A) = this.s2scp(s)
  famb(aa).s2scp(ss)
})
Run Code Online (Sandbox Code Playgroud)

而JavaScript中的绑定是我最喜欢的; 100%功能,精益和平均,但当然无类型:

var bind = function(famb){
    return state(function(s) {
        var a = this(s);
        return famb(a.value)(a.state);
    });
};
Run Code Online (Sandbox Code Playgroud)

<无耻>我在这里削减一些角落,但如果你对细节感兴趣,你可以在我的WP博客上找到它们.</ shameless>


use*_*306 6

这是关于 monad 的一个难以理解的事情:monad 是一种模式,而不是特定的类型。Monad 是一种形状,它们是一个抽象的接口(不是 Java 意义上的),而不是具体的数据结构。因此,任何示例驱动的教程都注定不完整和失败。[...] 理解 monad 的唯一方法是了解它们的本质:数学结构。

单子不是Daniel Spiewak 的隐喻


Java SE 8 中的 Monad

列出单子

interface Person {
    List<Person> parents();

    default List<Person> greatGrandParents1() {
        List<Person> list = new ArrayList<>();
        for (Person p : parents()) {
            for (Person gp : p.parents()) {
                for (Person ggp : p.parents()) {

                    list.add(ggp);
                }
            }
        }
        return list;
    }

    // <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
    default List<Person> greatGrandParents2() {
        return Stream.of(parents())
                .flatMap(p -> Stream.of(p.parents()))
                .flatMap(gp -> Stream.of(gp.parents()))
                .collect(toList());
    }
}
Run Code Online (Sandbox Code Playgroud)

也许单子

interface Person {
    String firstName();
    String middleName();
    String lastName();

    default String fullName1() {
        String fName = firstName();
        if (fName != null) {
            String mName = middleName();
            if (mName != null) {
                String lName = lastName();
                if (lName != null) {
                    return fName + " " + mName + " " + lName;
                }
            }
        }
        return null;
    }

    // <U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
    default Optional<String> fullName2() {
        return Optional.ofNullable(firstName())
                .flatMap(fName -> Optional.ofNullable(middleName())
                .flatMap(mName -> Optional.ofNullable(lastName())
                .flatMap(lName -> Optional.of(fName + " " + mName + " " + lName))));
    }
}
Run Code Online (Sandbox Code Playgroud)

Monad 是嵌套控制流封装的通用模式。即一种从嵌套的命令式习语中创建可重用组件的方法。

重要的是要了解 monad 不仅仅是具有平面映射操作的通用包装器类。例如,ArrayList使用flatMap方法不会是 monad。因为monad 法律禁止副作用。

Monad 是一种形式主义。它描述了结构,无论内容或含义如何。人们挣扎于与无意义(抽象)事物的联系。所以他们想出了不是单子的隐喻。

另请参阅: Erik Meijer 和 Gilad Bracha 之间的对话


Mor*_*Adi 5

理解monad的唯一方法是编写一堆组合器库,注意所产生的重复,然后亲自发现monad使您可以排除这种重复。在发现这一点时,每个人都为单子是什么建立了某种直觉……但是这种直觉不是您可以直接与他人交流的那种东西–似乎每个人都必须经历从某种具体事物推广到单子的相同经验。组合器库的示例。然而

在这里,我找到了一些学习蒙达斯的材料。

希望对您也有用。

代码提交

james-iry.blogspot

debasishg.blogspot