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


从标题可以猜到,本文的主要主题将是monads。我将尝试深入研究它们的结构和内部运作方式。在Java Optional的帮助下,我将尝试更详细地描述所有这些。在本文的最后,我将实现一个logmonad(一种类型的writermonad),然后描述每个主要代码片段的作用并提供其用法的简单示例。

为什么要学习Monads的工作原理? 首先,对我们使用的东西如何工作有一个基本的了解永远是一件好事。如果您是Java开发人员,则可能在不知道的情况下使用monad。这可能会让您感到惊讶,但是Java 8中最常见的两个功能是monad实现,即Stream和Optional。另外,函数式编程在当今变得越来越流行,因此我们可能会拥有更多类似的单子结构。这样,这些知识将变得更加非常有价值。

让我们从或多或少准确地描述一个monad开始。我认为,这件事很简单。

Monad只是endofunctors类别中的monoid

基于 一个 报价 从 “分类的工作数学家通过桑德斯的Mac巷”。

回到认真...

Monad是一个概念,而不是阶级或特质。当然,可以将其进一步实现为类或特质。具有泛型支持的几乎所有静态类型语言都是可能的。此外,我们可以将其视为包装器,将我们的值放在某个上下文中并允许我们对该值执行操作。在这种情况下,任何步骤的操作的输出就是下一步的操作的输入。

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

  • Stream (Java).
  • Optional/Option (Java/Scala).
  • Either (Scala).
  • Try (Scala).
  • IO Monad (Haskell). 在谈论单子时需要提到的最后一件事是他们的法律。如果我们想将我们的实现视为真正的monad,则必须服从它们。有三个定律:左身份,右身份和关联性。我认为,可能很难理解它们的实际含义。 现在,借助Optional,我将尝试更详细地解释上述法律。

但是首先有一些假设:

  • F是具有签名的函数:(T -> Optional<U>) = Optional<U>
  • G是带有签名的函数(A -> Optional<B>) = Optional<B>
  • FG = F.apply(value).flatMap(G)签名:(T -> Optional<B>) = Optional<B>。 左标识:如果我们创建一个新的monad并将其绑定到该函数,则结果应与将该函数应用于该值相同:Optional.of(value).flatMap(F).equals(F.apply(value))

正确的身份:将单位功能绑定到monad的结果应与创建新monad相同:Optional.of(value).flatMap(Optional::of).equals(Optional.of(value))

关联性-在函数应用程序链中,函数的嵌套方式无关紧要:

  • Optional<B> leftSide= Optional.of(value).flatMap(F).flatMap(G)
  • Optional<B> rightSide= Optional.of(value).flatMap(F.apply(value)
  • .flatMap(G))
  • leftSide.equals(rightSide). 现在,当我们了解基础知识之后,我们就可以专注于实现了。

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

unit用来包装我们的价值并有以下签名(T) = M<T>。 bind负责执行操作。在这里,我们传递了一个函数,该函数在上下文中对值进行操作,并以上下文中已包装的其他类型返回它。此方法应具有以下签名(T -> M<U>) = M<U>。 为了使它更易于理解,我将再使用Optional一次,并说明上述结构在其情况下的外观。

在这里,第一个条件立即得到满足,因为Optional是参数化类型。unit函数的作用是通过ofNullable和of方法来实现的。FlatMap 发挥bind 功能的作用。当然,在使用Optional的情况下,类型边界使我们可以使用比上面定义中更复杂的类型。

完成理论,让我们付诸实践

5673.png

Et voila,monad已实施。让我们详细描述我在这里所做的工作。

我们实现的基础是带有不可变字段“ value”的参数化类,该字段负责存储我们的值。第二个字段是“记录器”,它将负责我们monad的关键作用。然后,我们有了一个私有的构造函数,这使得除了通过包装 方法之外,无法以任何其他方式创建对象。

接下来,我们有两个基本的monad函数,分别是of(等效于unit)和flatMap(等效于bind),这将确保我们的实现以monad法则的形式满足要求的条件。后两种方法负责我们的monad效果,即它们负责将当前值记录到标准输出中。其中一个允许通过日志记录级别,另一个使用“ INFO”级别。

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

12.png

在上面的代码中,除了了解monad的工作原理以外,我们还可以看到一些使用monad的专家。

首先,我们将日志记录副作用封装在单子上下文中。这为我们的逻辑提供了一个抽象层。通过这种抽象,我们能够减少样板的数量。我们的代码变得更具说明性,更易于阅读和理解。最后,我们将所有操作组合到一个管道中。

另一方面,在示例的非单例部分中,我们可以看到所有实现细节。该代码具有较少的描述性和可重用性。而且,我们暴露了可能在将来引起一些问题的副作用,并使我们的代码可读性降低。如果我们决定处理函数调用中的错误,则将需要大量样板。

而且,我认为,单价代码比非单价代码更易于阅读和理解。

结论 Monad是一个非常有用的概念。也许我们许多人在日常生活中使用它。在本文中,我试图对它的理论基础和背后的逻辑提供清晰和描述性的解释。我实现了LogMonad,以证明它不是火箭科学,实际上实现它非常简单。在该示例中,我展示了monad的用法可能是什么样,这种操作的潜在优点是什么,以及它与普通方法调用有何不同。


原文链接:https://codingdict.com/