Kleisli Arrow与Scala中的作家。为什么不编译?

时间:2014-02-10 17:20:03

标签: scala monads scalaz kleisli

这是我之前question的后续内容。看起来我仍然没有得到它。现在我正在尝试编写返回 Writer monad的函数。

scala> val f = {x:Int => Writer("doing " + x + ";", x + 1)}
f: Int => scalaz.WriterT[scalaz.Id.Id,String,Int] = 

scala> Kleisli(f) >=> Kleisli(f)
:16: error: no type parameters for method apply: (f: A => M[B])scalaz.Kleisli[M,A,B] in object Kleisli exist so that it can be applied to arguments (Int => scalaz.WriterT[scalaz.Id.Id,String,Int])
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : Int => scalaz.WriterT[scalaz.Id.Id,String,Int]
 required: ?A => ?M

              Kleisli(f) >=> Kleisli(f)

为什么不编译?

1 个答案:

答案 0 :(得分:2)

当Scala编译器需要一个类似M[B]的类型并且你给它类似WriterT[Id, String, Int]的类型时,遗憾的是它不够智能,无法弄清楚你想要修复前两个类型的参数并使用WriterT[Id, String, _]的monad。

有几种可能的方法可以解决此限制。第一种是定义类型别名:

type StringWriter[A] = WriterT[Id, String, A]

现在你可以提供显式的类型参数(实际上你可以在没有别名的情况下做到这一点,但是type lambdas会使行变长两倍,十倍变得不可读):

scala> Kleisli[StringWriter, Int, Int](f) >=> Kleisli[StringWriter, Int, Int](f)
res0: scalaz.Kleisli[StringWriter,Int,Int] = Kleisli(<function1>)

Scalaz现在提供了一个更好的解决方案,通过Miles Sabin的"unapply trick"

val ff = Kleisli.kleisliU(f) >=> Kleisli.kleisliU(f)

kleisliU本质上只是Kleisli.apply的一个很好的版本,它在幕后使用一个新的类型类(名为Unapply)来引导类型推理系统以正确的方式分解WriterT[Id, String, Int]