gpt4 book ai didi

scala - scalaz 中的函数语法谜题

转载 作者:行者123 更新时间:2023-12-03 06:03:03 25 4
gpt4 key购买 nike

关注 Nick Partidge's presentation关于推导 scalaz ,我必须看看这个例子,这真是太棒了:

import scalaz._
import Scalaz._
def even(x: Int) : Validation[NonEmptyList[String], Int]
= if (x % 2 ==0) x.success else "not even: %d".format(x).wrapNel.fail

println( even(3) <|*|> even(5) ) //prints: Failure(NonEmptyList(not even: 3, not even: 5))

我试图了解 <|*|>方法是这样做的,这里是源代码:
def <|*|>[B](b: M[B])(implicit t: Functor[M], a: Apply[M]): M[(A, B)] 
= <**>(b, (_: A, _: B))

好的,这相当令人困惑(!) - 但它引用了 <**>方法,其声明如下:
def <**>[B, C](b: M[B], z: (A, B) => C)(implicit t: Functor[M], a: Apply[M]): M[C] 
= a(t.fmap(value, z.curried), b)

所以我有几个问题:
  • 为什么该方法似乎采用一种类型参数( M[B] )的高级类型,但可以通过 Validation (它有两种类型的参数)?
  • 语法 (_: A, _: B)定义函数 (A, B) => Pair[A,B]第二种方法期望的是:失败情况下 Tuple2/Pair 发生了什么?看不到元组!
  • 最佳答案

    类型构造函数作为类型参数
    M是 Scalaz 的主要皮条客之一的类型参数,MA , 表示拉皮条值的类型构造函数(又名高级类型)。此类型构造函数用于查找 Functor 的适当实例。和 Apply ,这是方法的隐含要求 <**> .

    trait MA[M[_], A] {
    val value: M[A]
    def <**>[B, C](b: M[B], z: (A, B) => C)(implicit t: Functor[M], a: Apply[M]): M[C] = ...
    }

    什么是类型构造函数?

    来自 Scala 语言引用:

    We distinguish between first-order types and type constructors, which take type parameters and yield types. A subset of first-order types called value types represents sets of (first-class) values. Value types are either concrete or abstract. Every concrete value type can be represented as a class type, i.e. a type designator (§3.2.3) that refers to a class1 (§5.3), or as a compound type (§3.2.7) representing an intersection of types, possibly with a refinement (§3.2.7) that further constrains the types of itsmembers. Abstract value types are introduced by type parameters (§4.4) and abstract type bindings (§4.3). Parentheses in types are used for grouping. We assume that objects and packages also implicitly define a class (of the same name as the object or package, but inaccessible to user programs).

    Non-value types capture properties of identifiers that are not values (§3.3). For example, a type constructor (§3.3.3) does not directly specify the type of values. However, when a type constructor is applied to the correct type arguments, it yields a first-order type, which may be a value type. Non-value types are expressed indirectly in Scala. E.g., a method type is described by writing down a method signature, which in itself is not a real type, although it gives rise to a corresponding function type (§3.3.1). Type constructors are another example, as one can write type Swap[m[_, _], a,b] = m[b, a], but there is no syntax to write the corresponding anonymous type function directly.


    List是一个类型构造函数。您可以申请类型 Int获取值类型, List[Int] ,它可以对一个值进行分类。其他类型构造函数接受多个参数。

    性状 scalaz.MA要求它的第一个类型参数必须是一个类型构造函数,它接受一个类型来返回一个值类型,语法为 trait MA[M[_], A] {} .类型参数定义描述了类型构造函数的形状,称为它的 Kind。 List据说有那种' * -> * .

    类型的部分应用

    但是怎么能 MA包装 Validation[X, Y] 类型的值?型号 Validation有种 (* *) -> * , 并且只能作为类型参数传递给声明为 M[_, _] 的类型参数.

    object Scalaz 中的这种隐式转换转换 Validation[X, Y] 类型的值到 MA :
    object Scalaz {
    implicit def ValidationMA[A, E](v: Validation[E, A]): MA[PartialApply1Of2[Validation, E]#Apply, A] = ma[PartialApply1Of2[Validation, E]#Apply, A](v)
    }

    反过来,它在 PartialApply1Of2 中使用了一个带有类型别名的技巧。部分应用类型构造函数 Validation ,修复错误的类型,但不应用成功的类型。
    PartialApply1Of2[Validation, E]#Apply最好写成 [X] => Validation[E, X] .我最近提议在 Scala 中添加这样的语法,它可能会在 2.9 中发生。

    将此视为与此等效的类型级别:
    def validation[A, B](a: A, b: B) = ...
    def partialApply1Of2[A, B C](f: (A, B) => C, a: A): (B => C) = (b: B) => f(a, b)

    这让您可以组合 Validation[String, Int]Validation[String, Boolean] ,因为两者共享类型构造函数 [A] Validation[String, A] .

    应用仿函数
    <**>需要类型构造函数 M必须有 Apply 的关联实例和 Functor .这构成了一个 Applicative Functor,它和 Monad 一样,是一种通过某种效果来构建计算的方法。在这种情况下,结果是子计算可能会失败(并且当它们失败时,我们会累积失败)。

    容器 Validation[NonEmptyList[String], A]可以包装类型为 A 的纯值在这个“效果”中。 <**>运算符采用两个有效值和一个纯函数,并将它们与该容器的 Applicative Functor 实例组合在一起。

    以下是 Option 的工作原理应用仿函数。这里的“效果”是失败的可能性。
    val os: Option[String] = Some("a")
    val oi: Option[Int] = Some(2)

    val result1 = (os <**> oi) { (s: String, i: Int) => s * i }
    assert(result1 == Some("aa"))

    val result2 = (os <**> (None: Option[Int])) { (s: String, i: Int) => s * i }
    assert(result2 == None)

    在这两种情况下,都有一个类型为 (String, Int) => String 的纯函数。 ,适用于有效的论据。请注意,结果与参数包装在相同的效果(或容器,如果您愿意)中。

    您可以在多个具有关联 Applicative Functor 的容器中使用相同的模式。所有的 Monad 都是自动的 Applicative Functor,但还有更多,比如 ZipStream .
    Option[A]Validation[X, A]都是 Monad,所以你也可以使用 Bind (又名 flatMap):
    val result3 = oi flatMap { i => os map { s => s * i } }
    val result4 = for {i <- oi; s <- os} yield s * i

    用 `<|**|>` 元组
    <|**|>真的很像 <**> ,但它为您提供了纯函数,可以根据结果简单地构建一个 Tuple2。 (_: A, _ B)(a: A, b: B) => Tuple2(a, b) 的简写

    超越

    这是我们为 Applicative 提供的捆绑示例和 Validation .我使用了稍微不同的语法来使用 Applicative Functor, (fa ⊛ fb ⊛ fc ⊛ fd) {(a, b, c, d) => .... }
    更新:但是在失败案例中会发生什么?

    what is happening to the Tuple2/Pair in the failure case?



    如果任何子计算失败,则永远不会运行提供的函数。只有在所有子计算(在这种情况下,传递给 <**> 的两个参数)都成功时才会运行它。如果是这样,它将这些组合成一个 Success .这个逻辑在哪里?这定义了 Apply [A] Validation[X, A] 的实例.我们要求类型 X 必须有 Semigroup可用,这是组合单个错误的策略,每个错误类型为 X , 转化为相同类型的聚合错误。如果您选择 String作为您的错误类型, Semigroup[String]连接字符串;如果您选择 NonEmptyList[String] ,来自每个步骤的错误被连接成一个更长的 NonEmptyList的错误。当两个 Failures 发生在下面时,这种连接发生合并,使用 运算符(它用隐式扩展为,例如, Scalaz.IdentityTo(e1).⊹(e2)(Semigroup.NonEmptyListSemigroup(Semigroup.StringSemigroup))
    implicit def ValidationApply[X: Semigroup]: Apply[PartialApply1Of2[Validation, X]#Apply] = new Apply[PartialApply1Of2[Validation, X]#Apply] {
    def apply[A, B](f: Validation[X, A => B], a: Validation[X, A]) = (f, a) match {
    case (Success(f), Success(a)) => success(f(a))
    case (Success(_), Failure(e)) => failure(e)
    case (Failure(e), Success(_)) => failure(e)
    case (Failure(e1), Failure(e2)) => failure(e1 ⊹ e2)
    }
    }

    Monad 或 Applicative,我该如何选择?

    还在读书? (是的。埃德)

    我已经展示了基于 Option 的子计算或 [A] Validation[E, A]可以与 Apply 结合使用或与 Bind .你什么时候会选择一个?

    当您使用 Apply ,计算的结构是固定的。将执行所有子计算;一个人的结果不能影响其他人。只有“纯”函数对发生的事情有一个概述。另一方面,一元计算允许第一个子计算影响后面的子计算。

    如果我们使用 Monadic 验证结构,第一次失败将使整个验证短路,因为不会有 Success值以提供给后续验证。但是,我们很高兴子验证是独立的,因此我们可以通过 Applicative 将它们组合起来,并收集我们遇到的所有失败。 Applicative Functors 的弱点变成了强项!

    关于scala - scalaz 中的函数语法谜题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2545248/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com