下面的看 尼克Partidge的演讲 在推导 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)

所以我有几个问题:

  1. 怎么来的方法似乎采取一个 高阶泛的类型 一种类型参数(M[B]),但可以得到通过 Validation (其中有两个类型参数)?
  2. 语法 (_: A, _: B) 定义功能 (A, B) => Pair[A,B] 其中第2方法的预期: 发生了什么Tuple2/对在失败的情况下?有没有多元组在视线!
有帮助吗?

解决方案

类型造型参数

M 是一种类型参数的一个Scalaz的主皮条客, MA, 那表示类型的构造(又名高阶泛类型)的拉皮条的价值。这种类型的构造是用来看的适当的实例 FunctorApply, ,这是隐含的要求的方法 <**>.

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] = ...
}

是什么类型的构造?

从卡拉语参考:

我们之间的区别的第一阶 类型和种类的构造, 采取类参数和产量类型。一个子集的第一阶类型称为 值类型的代表组 (第一类)的价值观。值的类型 无论是具体的或抽象的。每 具体价值的类型可以代表的 作为一个类型,即的类型 指定(第3.2.3),是指一个 1级(§5.3),或者作为一个复合型 (§3.2.7)代表一个交叉路口 的类型、可能的改进 (§3.2.7),进一步约束了 类型的itsmembers.抽象的价值 类型介绍的类型 参数(第4.4)和抽象的类型 绑定(第4.3).括号中的类型 用于分组。我们假设 目的和包也隐含地 定义一类(对相同的名称 对象或包装,但是 无法用户的程序)。

非值类型捕获性 标识符,不值 (§3.3).例如,一种类型 构造(§3.3.3)不直接 指定这种类型的价值观。但是, 当一个种类的构造应用到 正确的类型参数,它的产量 第一阶类型,这可能是一个 值的类型。无价值的类型 表示间接地在卡拉.E.g., 一个 方法种类的描述通过写作 下一个方法的签名,它在 本身不是一个真正的种类,虽然它 产生了一个相应的功能 类型(§3.3.1).类型的构造是 另一个例子,作为一个可以写入类型 交换[m[_,_],a、b]=m[b],但是 没有语法写的 应匿名的类型功能 直接。

List 是一种类型的构造。你可以适用的种类 Int 获得价值的类型, List[Int], ,这可以进行分类的价值。其他类型的构造超过一个参数。

该特质 scalaz.MA 要求它是第一类型的参数必须是一个种类的构造,需要一个单一的类型返回值的类型、语法 trait MA[M[_], A] {}.这类参数的定义中描述的形状类型的构造,这被称为同类。 List 是说有一种'* -> *.

局部应用程序的类型

但是怎么可能 MA 包裹值的类型 Validation[X, Y]?的类型 Validation 有一种 (* *) -> *, ,并且只能通过作为一个类型参数类型的参宣布喜欢 M[_, _].

这种隐含的转换 目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].我最近建议增加这种语法院,它可能会发生在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 必须有相关的实例 适用函子.这构成了一个应用性函,这样一个单,是一种方法来结构的计算通过一些效果。在这种情况下的效果是,子计算可能会失效(而当他们这样做,我们累积的失败).

容器 Validation[NonEmptyList[String], A] 可以换一个纯粹的价值的类型 A 在这个的'效果'.的 <**> 操作者需要两个effectful值,和一个纯粹的功能,并将它们与适用的函子实例,容器。

在这里,它是如何工作的 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, 目前适用于effectful的论点。请注意,结果是包裹在同样的效果(或容器,如果你喜欢),作为论据。

你可以使用相同的模式众多的容器有关的应用性函数.所有单元的自动适用的函,但还有更多像 ZipStream.

Option[A]Validation[X, A] 是两个单元,所以你也可以使用 Bind (aka flatMap):

val result3 = oi flatMap { i => os map { s => s * i } }
val result4 = for {i <- oi; s <- os} yield s * i

Tupling与`<|**|>`

<|**|> 是真的似的 <**>, 但它提供了纯粹的功能为你只是建立一个Tuple2的结果。 (_: A, _ B) 是的缩写 (a: A, b: B) => Tuple2(a, b)

这里是我们捆绑的例子 应用性验证.我用了一个稍微不同的语法使用适用的函子, (fa ⊛ fb ⊛ fc ⊛ fd) {(a, b, c, d) => .... }

更新:但是什么会发生故障的情况下?

发生了什么Tuple2/对在失败的情况下?

如果任何子计算的失败,所提供的功能是从来没有运行。这只是运行,如果所有子计算(在这种情况下,这两个论点过来 <**>)是成功的。如果是这样,它结合了这些成 Success.这是什么逻辑?这个定义 Apply 实例 [A] Validation[X, A].我们要求的类型X必须有一个 Semigroup 缴费,这是本战略相结合的各个错误,每个类型的 X, ,成为一个聚合的错误的类型相同。如果你选择 String 作为你的错误类型, Semigroup[String] 连接在串;如果你选择 NonEmptyList[String], ,错误(s)从每个步骤都是连接成一个更长的 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)
  }
}

单或适用的,我怎么选择?

还在读书?(是的。Ed)

我已经显示,子计算基础 Option[A] Validation[E, A] 可以结合任 ApplyBind.的时候你会选择一个比其他?

当你使用 Apply, 结构计算的,是固定的。所有子计算中将执行;结果一个人不能影响的其他人。只有'纯'功能已经概述了发生了什么事。元计算,另一方面,允许第一次计算的影响后来的。

如果我们用一个单子验证的结构,第一失败将会短路了整个验证,因为不会有 Success 值进入随后的验证。但是,我们是快乐子的验证是独立的,所以我们可以将它们结合起来,通过应用,并收集所有失败,我们遇到的问题。薄弱的应用函已成为一种力量!

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top