k47.cz    — každý den dokud se vám to nezačne líbit
foto Praha výběr povídky kultura | twitter FB


Kleisli arrow

23. 3. 2012 — k47 (CC by-sa)

Představte si, že mám dvě funkce:

val f: String => List[File]
val g: File => List[Tweet]

A potřeboval bych je spojit do následující funkce:

val r: String => List[Tweet]

Tedy něco ve stylu monadického flatMap: f flatMap g. Takhle přímo by to fungovalo, kdyby f nebyla funkce String => List[File], ale rovnou List[File].

Implementace bez použití knihovny ScalaZ by mohla vypdat následovně:

val r = (arg: String) => for { ff <- f(arg); gg <- g(ff) } yield gg
val r = (arg: String) => f(arg) flatMap { g(_) }

ScalaZ nabízí Kleisli Arrow, který tento problém krásně vyřeší point-free stylem:

val r = kleisli(f) >=> g

Takto se dá řetězit i víc funkcí: kleisli(a) >=> b >=> c a výsledek je pořád "plochá" funkce typu A => M[D] (nikoli A => M[M[M[D]]]).

<=< funguje stejně ale v opačném směru, takže a >=> b <=< x je stejné jako (a >=> b) <=< x nebo x >=> a >=> b.

Pak je k dispozici ještě operátor =<<, pro který platí, že f =<< list je obdoba list.flatMap(f).

Následující dva řádky kódu se chovají stejně, ale přistupují k věci jinak. Kleisli Arrow nejdřív pomocí kombinátorů spojí několik jednoduchých funkcí a pak na ně operátorem =<< aplikuje kolekci; druhý řádek začíná s kolekcí a pak na ni popořadě aplikuje jednotlivé funkce.

f >=> g =<< List("/home/adam-k/data/dir1", "/home/adam-k/data/dir2")
List("/home/adam-k/data/dir1", "/home/adam-k/data/dir2") flatMap f flatMap g

Více k tématu zde:

https://wiki.scala-lang.org/display/SW/Kleisli+Monad http://www.cakesolutions.net/teamblogs/2011/09/16/kleisli-arrows/ * http://www.cakesolutions.net/teamblogs/2011/09/18/kleisli-arrows-ii/


PS: A pokud máte rádi unicode, tak je synonymem funkce kleisli.

píše k47 & hosté, ascii@k47.cz