飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • UEditor使用文档
  • AngularJS教程
  • ThinkPHP5.0教程

什么是Monad?Java 开发人员的基本理论

时间:2021-12-10  作者:匿名  

monad 是一个概念,源自数学的一部分,称为 范畴论,而不是类或特征。在本文中,我将尝试解释其结构和内部工作原理。通过使用Java 中的Optional ,我将尝试以更易于理解的方式描述所有这些。我还将实现一个基本的 monad 以更好地理解它们的工作原理,并以一个简短的使用示例结束,以展示 monad 相对于非 monad 方法的优势。


为什么要学习 Monads 的工作原理?

首先,对我们使用的东西如何工作有一个基本的了解总是好的。如果您是 Java 开发人员,您可能会使用 monad,甚至可能不知道。您可能会感到惊讶,但两个最著名的Java 8 特性,即Stream和Optional是 monad 实现。 

另外,现在函数式编程越来越流行,所以我们可能会有更多类似的单子结构。在这种情况下,了解 monad 是什么以及它是如何工作的可能会变得更加有价值。 

让我们从描述 monad 是什么开始——或多或少准确。在我看来,这里的问题相当简单。

什么是Monad?

阅读介绍后,您就会知道 monad 是范畴论中的一个概念。在软件世界中,它可以在任何具有泛型支持的静态类型语言中实现为类或特征。此外,我们可以将它视为一个包装器,它将我们的值放在某个上下文中,并允许我们对值执行操作,特别是返回包装在同一上下文中的值的操作。此外,我们可以以这样的方式链接操作,即任何步骤的操作输出都是下一步操作的输入。


现代编程语言中的 monad 示例:

  • 流(Java)。

  • 可选/选项(Java/Scala)。

  • 要么(斯卡拉)。

  • 试试(斯卡拉)。

  • IO Monad (Haskell)。

Monad定律

在谈到 monad 时,最后需要提及的是它们的定律。如果我们想将我们的实现视为真正的 monad,我们必须遵守它们。存在三个定律:左恒等式、右恒等式和结合律。在我看来,理解它们的实际含义可能有点困难。

现在,在 Optional的帮助下,我将尝试更详细地解释上述规律。

但首先有几个假设:

  • f 是从类型 T 到类型 Optional<R> 的函数映射

  • g 是从类型 R 到类型 Optional<U> 的函数映射


1、Left identity

如果我们创建一个新的 monad 并将其绑定到函数,结果应该与将函数应用于值相同。

Optional<String> leftIdentity = 域名(x).flatMap(f);
Optional<String> mappedX = 域名y(x);
assert 域名ls(mappedX);

2、Right identity

将单元函数绑定到 monad 的结果应该与创建新 monad 的结果相同。

Optional<Integer> rightIdentity = 域名(x).flatMap(Optional::of);
Optional<Integer> wrappedX = 域名(x);
assert 域名ls(wrappedX);

3、Associativity

在函数应用链中,函数如何嵌套应该无关紧要。

Optional<Long> leftSide = 域名(x).flatMap(f).flatMap(g);
Optional<Long> rightSide = 域名(x).flatMap(v -> 域名y(v).flatMap(g));
assert 域名ls(rightSide);

Monad的创建

现在,当我们了解了基础知识后,我们就可以专注于实施了。

我们需要的第一件事是参数化类型 M<T>,它是我们类型 T 值的包装器。我们的类型必须实现两个函数:

  • of ( unit ) 用于包装我们的值并具有以下签名M<T>(T)。

  • flatMap ( bind ) 负责执行操作。在这里,我们传递了一个函数,该函数对我们的上下文中的值进行操作,并使用已经包装在上下文中的另一种类型返回它。此方法应具有以下签名M<U> (T -> M<U>)。

为了让它更容易理解,我将再使用一次Optional并展示在这种情况下上述结构的样子。

在这里,第一个条件立即得到满足,因为Optional是一个参数化类型。该单元功能的作用是满足ofNullable和的方法。FlatMap 扮演绑定函数的角色。当然,在Optional的情况下,类型边界允许我们使用比上面定义更复杂的类型。

说完了理论,让我们实施

import 域名域名tion;
public final class WrapperMonad<T> {
    private final T value;
    private WrapperMonad(T value) {
        域名e = value;
    }
    static <T> WrapperMonad<T> of(T value) {
        return new WrapperMonad<>(value);
    }
    <U> WrapperMonad<U> flatMap(Function<T, WrapperMonad<U>> function) {
        return 域名y(value);
    }
  // For sake of asserting in Example
    boolean valueEquals(T x) {
        return 域名ls(x);
    }
}

monad 实现了。让我们详细描述一下我在这里做了什么。

这里究竟发生了什么

我们实现的基础是带有名为“value”的不可变字段的参数化类,它负责存储我们的值。然后,我们有一个私有构造函数,这使得除了通过我们的包装方法 - of之外的任何其他方式创建对象都是不可能的。

接下来,我们有两个基本的单子功能,即中(相当于单位)和flatMap(相当于绑定),这将保证我们的执行符合所要求的条件的单子法律形式。

有了所描述的功能,现在是使用示例的时候了。所以在这里。

import 域名域名tion;
public class Example {
    public static void main(String[] args) {
        int x = 2;
        // Task: performing operation, returning wrapped value, over the value inside the container object.
        // Non-Monad
        Function<Integer, Wrapper<String>> toString = i -> new Wrapper<>(域名ring());
        Function<String, Wrapper<Integer>> hashCode = str -> new Wrapper<>(域名Code());
        Wrapper<Integer> wrapper = new Wrapper<>(x);
        Wrapper<String> stringifyWrapper = 域名y(域名e);
        // One liner - Wrapper<Integer> hashCodedWrapper = 域名y(域名y(x).value);
        Wrapper<Integer> hashCodedWrapper = 域名y(域名e);
        // Monad
        Function<Integer, WrapperMonad<String>> toStringM = i -> 域名(域名ring());
        Function<String, WrapperMonad<Integer>> hashCodeM = str -> 域名(域名Code());
        WrapperMonad<Integer> hashCodedWrapperMonadic = 域名(x)
                .flatMap(toStringM)
                .flatMap(hashCodeM);
        assert 域名eEquals(域名e);
        域名tln("Values inside wrappers are equal");
    }
}

在上面的代码中,除了看到 monad 是如何工作的,我们还可以看到使用它们的一些优点。

在 monadic 部分,所有操作都组合成一个单一的执行管道,这使得代码更具声明性,更易于阅读和理解。此外,如果我们决定有一天能加入错误处理逻辑,它可以很好地封装内部的和flatMap方法。

另一方面,在示例的非 monadic 部分,我们有一个不同的包私有字段值的设计,我们需要一种从包装器外部访问值的方法,这打破了封装。

总结

Monad是一个非常有用和强大的概念,可能我们很多人在日常工作中都会使用它。我试图对其背后的理论基础和逻辑进行清晰的描述性解释。我实现了一个自定义 monad 来表明它不是一个复杂的结构。在上面的例子中,我展示了 monad 的用法,这种方法的潜在优点是什么,以及它与普通方法调用的区别。

标签:MonadJava
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。