スカラズ:Cokleisli 合成のユースケースのリクエスト
-
23-09-2019 - |
質問
この質問は炎上を目的としたものではありません。明らかなように、私は見てきました スカラズ 最近。私は理解しようとしています なぜ ライブラリが提供する機能の一部が必要です。ここに何かがあります:
import scalaz._
import Scalaz._
type NEL[A] = NonEmptyList[A]
val NEL = NonEmptyList
何が起こっているかを確認するために、関数に 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) }
次に、それらを経由して結合します コックライスリ そして、 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 値は、最終的な NEL 内の (String) 要素の文字数です。私には次の 2 つのことが思い浮かびます。
- 関連するメソッド シグネチャから、NEL がこのように削減されることをどのようにして知ることができたのでしょうか?(結果は期待していませんでした まったく)
- これにはどういう意味があるのでしょうか?かなりシンプルでわかりやすいユースケースを抽出できるでしょうか?
この質問は、次のような素敵な人に対する、ベールの薄い嘆願です。 レトロニム この強力なライブラリが実際にどのように機能するかを説明します。
解決
は結果を理解するには、Comonad[NonEmptyList]
インスタンスを理解する必要があります。 Comonad[W]
基本的に(Scalazの実際のインターフェースは少し異なりますが、これは説明に役立ちます)3つの機能を提供しています:
map: (A => B) => W[A] => W[B]
copure: W[A] => A
cojoin: W[A] => W[W[A]]
そこで、Comonad
がAとのそれぞれ、「ヘッド」要素(W
)、我々は要素ごとに1つの容器(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
以外のそれらについて何も知らない、2つの関数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に定義されています.Tree と 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
ツリーを取得し、葉からの最大距離で注釈各ノードにそれのコピーを返します。