Scalaz:请求使用的情况下为组成Cokleisli
-
23-09-2019 - |
题
这个问题并不是火焰-诱饵!因为它可能是显而易见的,我已经看 Scalaz 最近。我试着去理解 为什么 我需要一些功能,该功能的图书馆提供的。这里的东西:
import scalaz._
import Scalaz._
type NEL[A] = NonEmptyList[A]
val NEL = NonEmptyList
我把一些释放的发言,在我的功能来看看发生了什么事(旁白:我会怎么做,如果我试图避免的副作用那样的?).我的职责是:
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) }
然后我把他们通过一个 cokleisli 并通过在一个 NEL[Int]
val k = cokleisli(f) =>= cokleisli(g)
println("RES: " + k( NEL(1, 2, 3) ))
这是什么字?
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
RES值的字符数(String)元素在最终NEL.两件事情发生的对我说:
- 我怎么可能知道,我NEL会减少以这种方式从方法的签名吗?(我没想到结果 在所有)
- 什么是这?可以相当简单和易于执行的使用情况以蒸馏我吗?
这个问题是一个精简的含蓄请求对于一些可爱的人像 retronym 来解释如何强大的图书馆的实际工作。
解决方案
要了解该结果,需要了解的 Comonad[NonEmptyList]
实例。 Comonad[W]
基本上提供了三种功能(实际接口在Scalaz是一点点不同,但这有助于解释):
map: (A => B) => W[A] => W[B]
copure: W[A] => A
cojoin: W[A] => W[W[A]]
所以, Comonad
提供一个接口,对一些容器 W
有一位杰出的"头"的元素(copure
)和一种方式暴露的内部结构的容器以便使我们得到一个容器,每件(cojoin
),每一个元素在头。
的方式,这是实施 NonEmptyList
是, copure
返回头的名单, cojoin
返回的列表,列表,这个清单在头部和所有尾巴的这个列表中的尾巴。
例(我也缩短了 NonEmptyList
要 Nel
):
Nel(1,2,3).copure = 1
Nel(1,2,3).cojoin = Nel(Nel(1,2,3),Nel(2,3),Nel(3))
的 =>=
功能是coKleisli组成。你会如何撰写的两个功能 f: W[A] => B
和 g: W[B] => C
, 知道什么关于他们比其他 W
是一个 Comonad
?输入型的 f
和输出种类型的 g
不兼容。但是,你可以 map(f)
得到 W[W[A]] => W[B]
然后撰写的, g
.现在,给一个 W[A]
, 你可以 cojoin
它得到的 W[W[A]]
进入这一函数。所以,唯一合理的组成是一个功能 k
这并如下:
k(x) = g(x.cojoin.map(f))
所以你的非空清单:
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
其他提示
Cojoin也被定义为 scalaz.树 和 scalaz.TreeLoc.这个可以 利用 找到一流的所有的路径从根的树到每叶节点。
def leafPaths[T](tree: Tree[T]): Stream[Stream[T]]
= tree.loc.cojoin.toTree.flatten.filter(_.isLeaf).map(_.path)
使用coKleisli箭头组成,我们可以做到这一点,例如:
def leafDist[A] = (cokleisli(leafPaths[A]) &&& cokleisli(_.rootLabel))
=>= (_.map(s => (s._2, s._1.map(_.length).max)))
leafDist
需要一棵树,并返回的副本,它与每个节点的附加说明与其最大的距离从一片叶子。