Scalaz:请求Cokleisli组合的用例

oxb*_*kes 17 functional-programming scala scala-2.8 scalaz

这个问题并不意味着火焰诱饵!可能很明显,我最近一直在关注Scalaz.我试图理解为什么我需要库提供的一些功能.这是一些东西:

import scalaz._
import Scalaz._
type NEL[A] = NonEmptyList[A]
val NEL = NonEmptyList
Run Code Online (Sandbox Code Playgroud)

我在我的函数中放了一些println语句来查看发生了什么(除了:如果我试图避免这样的副作用,我会怎么做?).我的职责是:

val f: NEL[Int] => String    = (l: NEL[Int]) => {println("f: " + l); l.toString |+| "X" }
val g: NEL[String] => BigInt = (l: NEL[String]) => {println("g: " + l);  BigInt(l.map(_.length).sum) }
Run Code Online (Sandbox Code Playgroud)

然后我通过cokleisli将它们组合在一起并传入NEL[Int]

val k = cokleisli(f) =>= cokleisli(g)
println("RES: "  + k( NEL(1, 2, 3) ))
Run Code Online (Sandbox Code Playgroud)

这打印什么?

f: NonEmptyList(1, 2, 3)
f: NonEmptyList(2, 3)
f: NonEmptyList(3)
g: NonEmptyList(NonEmptyList(1, 2, 3)X, NonEmptyList(2, 3)X, NonEmptyList(3)X)
RES: 57
Run Code Online (Sandbox Code Playgroud)

RES值是最终NEL中(String)元素的字符数.我发生了两件事:

  1. 我怎么能知道我的NEL会​​以这种方式从涉及的方法签名中减少?(我没想到结果在所有)
  2. 这有什么意义?可以为我提炼一个相当简单和易于使用的用例吗?

这个问题是对一些可爱的人(如反名词)的一种薄薄的恳求,以解释这个强大的图书馆实际上是如何运作的.

Apo*_*isp 18

要了解结果,您需要了解Comonad[NonEmptyList]实例.Comonad[W]本质上提供了三个函数(Scalaz中的实际接口有点不同,但这有助于解释):

map:    (A => B) => W[A] => W[B]
copure: W[A] => A
cojoin: W[A] => W[W[A]]
Run Code Online (Sandbox Code Playgroud)

因此,Comonad为某个容器提供了一个接口,该容器W具有一个独特的"head"元素(copure)和一种暴露容器内部结构的方式,这样我们每个元素(cojoin)获得一个容器,每个容器的头部都有一个给定的元素.

实现它的方法NonEmptyListcopure返回列表的头部,并cojoin返回列表列表,其中列表位于此列表的尾部,此列表的所有尾部都在尾部.

示例(我缩短NonEmptyListNel):

Nel(1,2,3).copure = 1
Nel(1,2,3).cojoin = Nel(Nel(1,2,3),Nel(2,3),Nel(3))
Run Code Online (Sandbox Code Playgroud)

=>=功能是coKleisli组成.你会如何撰写两个函数f: W[A] => Bg: W[B] => C,知道见死不救比其他WComonad?输入类型f和输出类型g不兼容.但是,你可以map(f)得到W[W[A]] => W[B]然后用它来组成g.现在,给定一个W[A],您可以将cojoinW[W[A]]输入到该函数中.因此,唯一合理的组合是k执行以下操作的功能:

k(x) = g(x.cojoin.map(f))
Run Code Online (Sandbox Code Playgroud)

所以对于你的非空列表:

g(Nel(1,2,3).cojoin.map(f))
= g(Nel(Nel(1,2,3),Nel(2,3),Nel(3)).map(f))
= g(Nel("Nel(1,2,3)X","Nel(2,3)X","Nel(3)X"))
= BigInt(Nel("Nel(1,2,3)X","Nel(2,3)X","Nel(3)X").map(_.length).sum)
= BigInt(Nel(11,9,7).sum)
= 27
Run Code Online (Sandbox Code Playgroud)


ret*_*nym 9

Cojoin也是为scalaz.Treescalaz.TreeLoc定义的.这可以被利用来查找从树的根到每个叶节点的所有路径的流.

def leafPaths[T](tree: Tree[T]): Stream[Stream[T]]
  = tree.loc.cojoin.toTree.flatten.filter(_.isLeaf).map(_.path)
Run Code Online (Sandbox Code Playgroud)

使用coKleisli箭头组合,我们可以这样做,例如:

def leafDist[A] = (cokleisli(leafPaths[A]) &&& cokleisli(_.rootLabel))
  =>= (_.map(s => (s._2, s._1.map(_.length).max)))
Run Code Online (Sandbox Code Playgroud)

leafDist 获取一个Tree并返回它的副本,每个节点都注释了它与叶子的最大距离.