什么是适用的?Java开发人员的基础理论

你是一个想了解应用程序背后的理论的Java开发人员吗?在这里你会找到一步一步的教程,帮助你理解它们。

Applicative只是另一个在意义和历史上类似于函子单子。我在以前的文章中已经讨论过这两个问题,我认为现在是时候结束这个关于最常见的函数抽象的小系列了。除了解释一些细节和理论,我将实现一个简单的应用程序。我还将使用Optional,希望是最后一次,来展示应用程序给我们带来的优势。

我们为什么要关心应用程序?

首先,应用程序是函子和单子之间的中间构造。它们比函子更强大,但不如单子强大。应用程序也非常适合执行各种上下文无关的计算,如解析器或可遍历实例。

此外,所有应用程序都是函子,如果您有一些使用函子的经验,这可能会使应用程序的实现更容易。由于这种关系,应用程序可以使从函子到单子的整个过程更容易,在两个概念之间起到桥梁的作用。

此外,它们通常比单子更容易写。在像Scala或Haskell这样的函数式语言中,应用程序比单子有更多的实例。

什么是应用程序?

应用函子,或简称为应用函子,是一个主要的函数式编程概念。它是在2008年由康纳·麦克布莱德罗斯·帕特森在他们的论文中有效果的应用程序设计。它们在范畴理论中的间接对应物被称为lax幺半群函子。此外,请记住所有的应用程序都是函子。当我们开始讨论时,这种关系将产生重大影响适用法律和方法——这主要意味着函子所要求的所有法则也必须被应用程序所满足。

在软件世界中,与单子类似,应用程序的主要焦点是将值包装在特定的上下文中,然后执行操作——确切地说,是将操作包装在与值相同的上下文中。与单子相反,应用程序不允许像单子一样的链式操作,一个操作的输出是另一个操作的输入。不幸的是,它在Java中不太容易实现,所以不管怎样,我们基本上只能拥有这样的能力。此外,与函子不同,应用程序允许我们对计算进行排序。


免费学习资料

适用法律

与前面描述的两种数据类型一样,应用程序也需要遵守规则。事实上,应用程序具有最多数量的此类法律,即:身份, 同形交换作文。在我看来,可应用的法则也是乍一看最难理解的,尤其是同形交换在我描述的所有三种数据类型中。

通常,在我们开始之前有几个假设:

  • f是从类型映射的函数T打字R
  • g是从类型映射的函数R打字U

身份
对用pure包装的值应用identity函数应该总是返回一个不变的值。

Applicative<Integer> identity = Applicative.pure(x).apply(Applicative.pure(Function.identity()));
boolean identityLawFulfilled = identity.valueEquals(Applicative.pure(x));

同形
将包装的函数应用于包装的值应该与将函数应用于值,然后使用pure包装结果产生相同的结果。

Applicative<String> leftSide = Applicative.pure(x).apply(Applicative.pure(f));
Applicative<String> rightSide = Applicative.pure(f.apply(x));
boolean homomorphismLawFulfilled = leftSide.valueEquals(rightSide);

 交换
将包装函数f应用于包装值应该与将包装函数f的值作为参数提供给另一个函数相同

// As far as I can tell it is as close, to original meaning of this Law, as possible in Java
Applicative<String> interchangeLeftSide = Applicative.pure(x).apply(Applicative.pure(f));
Supplier<Integer> supplier = () -> x;
Function<Supplier<Integer>, String> tmp = i -> f.apply(i.get());
Applicative<String> interchangeRightSide = Applicative.pure(supplier).apply(Applicative.pure(tmp));
boolean interchangeLawFulfilled = interchangeLeftSide.valueEquals(interchangeRightSide);

作文
应用包装函数f,然后应用包装函数g,应该给出与一起应用包装函数f和g的合成相同的结果

// As far as I can tell it should be in line with what is expected from this Law
Applicative<Long> compositionLeftSide = Applicative.pure(x).apply(Applicative.pure(f)).apply(Applicative.pure(g));
Applicative<Long> compositionRightSide = Applicative.pure(x).apply(Applicative.pure(f.andThen(g)));
boolean compositionLawFulfilled = compositionLeftSide.valueEquals(compositionRightSide);

此外,因为Applicative是函子的扩展,所以您的实例应该满足函子的两个定律:身份作文。幸运的是,函子定义强加的两个定律已经在应用性的四个定律中,所以它们应该由应用性定义本身来满足。

现在你知道了你必须满足的法律,我可以开始谈论你到底需要什么来实现你的应用程序。

创建应用程序

  1. 我们需要什么来实现应用程序

    你首先需要的是参数化类型A<T >。参数化类型是所有在结构上类似于应用程序、单子和函子的数据类型的基础。此外,您将需要两种方法:

    • 应用(或者美国联合通讯社(Associated Press的缩写))负责执行操作。这里,您传递了一个已经包装在我们的上下文中的函数,它对我们的上下文中的值进行操作。此方法应该具有以下签名M<U> (M<T -> U >).
    • 纯的它用于包装您的值,并具有以下签名M<T>(T).

    此外,因为所有应用程序都是函子,所以您会得到一个地图带签名的方法M<R> (T -> R)根据定义。

    请注意:

    在这种情况下,还有第二个可能更常见的适用的等价词。代替apply方法,我们有一个带签名的product方法M<(T,U)>(M<T >,M<U >)。这些应用程序必须遵守不同的法律,即:结合性左侧标识正确的身份。它们的描述和示例包含在GitHub知识库.

    知道了一个应用程序需要什么,我就可以开始实现它了。

  2. 实现应用程序

public final class OptionalApplicative<T> implements Functor<T> {

    private final Optional<T> value;

    private OptionalApplicative(T value) {
        this.value = Optional.of(value);
    }

    private OptionalApplicative() {
        this.value = Optional.empty();
    }

    <U> OptionalApplicative<U> apply(OptionalApplicative<Function<T, U>> functionApplicative) {
        Optional<U> apply = functionApplicative.value.flatMap(value::map);
        return apply.map(OptionalApplicative::new).orElseGet(OptionalApplicative::new);
    }

    static <T> OptionalApplicative<T> pure(T value) {
        return new OptionalApplicative<>(value);
    }

    @Override
    public <R> Functor<R> map(Function<T, R> f) {
        return apply(pure(f));
    }

    // For sake of asserting in Example and LawsValidator
    public boolean valueEquals(Optional<T> s) {
        return value.equals(s);
    }

    public boolean valueEquals(OptionalApplicative<T> s) {
        return this.valueEquals(s.value);
    }
}
  1. 上面,你可以看到现成的应用程序实现,但为什么它看起来像这样呢?
  2. 描述应用实现

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

    接下来是两个应用程序特有的方法纯的用于在应用上下文中包装值,并应用用于使用包装函数包装值。两者都是使用可选的及其方法实现的,因此它们是完全空安全的。

    那你就有办法了地图来自函子,它是一个“额外的”方法,如果你对应用程序内部的值执行一些操作感兴趣,它可能会变得有用。

  3. 适用用法示例

    让我们继续介绍一个简单的应用性用法的例子,以及为什么它可能比非应用性方法更好的简短描述。

public class Example {

    public static void main(String[] args) {
        int x = 2;
        Function<Integer, String> f = Object::toString;
        // Task: applying function wrapped in context to value inside that context.

        // Non-applicative
        Optional<Integer> ox = Optional.of(x);
        Optional<Function<Integer, String>> of = Optional.of(f);
        Optional<String> ofx = ox.flatMap(d -> of.map(h -> h.apply(d)));
        // One liner -> Optional.of(x).flatMap(d -> Optional.of(f).map(h -> h.apply(d)));

        // Applicative
        OptionalApplicative<Integer> ax = OptionalApplicative.pure(x);
        OptionalApplicative<Function<Integer, String>> af = OptionalApplicative.pure(f);
        OptionalApplicative<String> afx = ax.apply(af);
        // One liner -> OptionalApplicative.pure(x).apply(OptionalApplicative.pure(f));

        if (afx.valueEquals(ofx)) {
            System.out.println("Values inside wrappers are equal");
        } else {
            throw new RuntimeException("Values inside wrappers are not equal");
        }
    }
}
  1. 上面你可以看到应用抽象比简单的基于可选的方法可能带来的好处。首先,基于应用的代码比可选代码更简单,更容易理解,它不需要任何复杂的东西,比如嵌入式映射调用。事实上,用户甚至不知道他们正在使用具有可选特性的应用程序,所以我能够提供更好的封装。在我看来,使用这种抽象的好处超过了编写抽象的成本。

总结

尽管应用程序是一个不如单子强大的概念,但它可能是一个很好的解决方案,尤其是当您需要处理上下文无关的计算时。当您必须编写解析器或可遍历性时,它们非常有用。此外,作为函子和单子之间的中间概念,它们可以使您更容易地了解范畴理论数据类型。

最后

感谢阅读,点赞收藏+关注!更多的java课程学习资料博主已经整理好了,有兴趣学习的朋友可以私信博主“学习”即可获得免费资料!!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值