这是我之前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)
为什么不编译?
答案 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]
。