Scalaz: solicitud de casos de uso para la composición Cokleisli
-
23-09-2019 - |
Pregunta
Esta pregunta no se entiende como la llama cebo! Como podría ser evidente, he estado buscando en Scalaz recientemente. Estoy tratando de entender ¿Por qué Necesito algunas de las funcionalidades que ofrece la biblioteca. de aquí algo:
import scalaz._
import Scalaz._
type NEL[A] = NonEmptyList[A]
val NEL = NonEmptyList
Me puso algunas sentencias println en mis funciones para ver lo que estaba pasando ( a un lado: ¿qué habría hecho si yo estaba tratando de efectos secundarios que evitar como ?). Mis funciones son:
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) }
A continuación, las combino a través de un cokleisli y paso en un NEL[Int]
val k = cokleisli(f) =>= cokleisli(g)
println("RES: " + k( NEL(1, 2, 3) ))
Lo que hace esta impresión?
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
El valor RES es el recuento de caracteres de los elementos (String) en el NEL final. Hay dos cosas que se me ocurren:
- ¿Cómo podría haber sabido que mi NEL se va a reducirse de este modo de las firmas de los métodos involucrados? (Yo no estaba esperando el resultado en absoluto )
- ¿Cuál es el punto de esto? ¿Puede un simple y razonablemente caso de uso fácil de seguimiento destilar para mí?
Esta pregunta se hace un llamamiento velado escasamente por alguna persona encantadora como retronym para explicar cómo esta poderosa biblioteca realmente funciona.
Solución
Para entender el resultado, es necesario entender la instancia Comonad[NonEmptyList]
. Comonad[W]
proporciona esencialmente tres funciones (la interfaz real en Scalaz es un poco diferente, pero esto ayuda con la explicación):
map: (A => B) => W[A] => W[B]
copure: W[A] => A
cojoin: W[A] => W[W[A]]
Así, Comonad
proporciona una interfaz para algunos W
contenedor que tiene un elemento distinguido "cabeza" (copure
) y una forma de exponer la estructura interna del recipiente de modo que obtenemos un contenedor por elemento (cojoin
), cada uno con una elemento dado en la cabeza.
La forma en que esto se implementa para NonEmptyList
es que copure
vuelve la cabeza de la lista, y cojoin
devuelve una lista de listas, con esta lista en la cabeza y todas las colas de esta lista en la cola.
Ejemplo (estoy acortando NonEmptyList
a Nel
):
Nel(1,2,3).copure = 1
Nel(1,2,3).cojoin = Nel(Nel(1,2,3),Nel(2,3),Nel(3))
La función =>=
es la composición coKleisli. ¿Cómo compones dos funciones f: W[A] => B
y g: W[B] => C
, sin saber nada acerca de ellos aparte de eso es un W
Comonad
? El tipo de entrada de f
y el tipo de salida de g
no son compatibles. Sin embargo, se puede conseguir map(f)
W[W[A]] => W[B]
y luego que la componen con g
. Ahora, dado un W[A]
, puede cojoin
para obtener el W[W[A]]
de alimentación en esa función. Por lo tanto, la composición única razonable es un k
función que hace lo siguiente:
k(x) = g(x.cojoin.map(f))
Así que para su lista no vacía:
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
Otros consejos
Cojoin también se define para scalaz .Tree y scalaz. Treeloc . Esto puede ser href="http://scalaz.googlecode.com/svn/continuous/latest/browse.sxr/scalaz/example/ExampleTree.scala.html#13383" explotada encontrar una corriente de todos los caminos de la raíz del árbol a cada nodo hoja.
def leafPaths[T](tree: Tree[T]): Stream[Stream[T]]
= tree.loc.cojoin.toTree.flatten.filter(_.isLeaf).map(_.path)
El uso de coKleisli flecha composición, podemos hacer esto, por ejemplo:
def leafDist[A] = (cokleisli(leafPaths[A]) &&& cokleisli(_.rootLabel))
=>= (_.map(s => (s._2, s._1.map(_.length).max)))
leafDist
toma un árbol y devuelve una copia de la misma con cada nodo anotado con su distancia máxima de una hoja.