3

This is a followup to my previous question. Looks like I still did not get it. Now I am trying to compose functions that return 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)

Why doesn't it compile ?

Community
  • 1
  • 1
Michael
  • 41,026
  • 70
  • 193
  • 341
  • 1
    `argument expression's type is not compatible with formal parameter type; found : Int => scalaz.WriterT[scalaz.Id.Id,String,Int] required: ?A => ?M` – Robert Harvey Feb 10 '14 at 17:21

1 Answers1

2

When the Scala compiler expects a type shaped like M[B] and you give it something like WriterT[Id, String, Int], it's unfortunately just not smart enough to figure out that you want to fix the first two type parameters and use the monad for WriterT[Id, String, _].

There are a few possible ways to work around this limitation. The first is to define a type alias:

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

Now you can provide explicit type parameters (in fact you could do this without the alias, but the type lambdas would make the line twice as long and ten times as unreadable):

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

Scalaz now provides a nicer solution, though, via Miles Sabin's "unapply trick":

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

kleisliU is essentially just a nice version of Kleisli.apply that uses a new type class (named Unapply) behind the scenes to guide the type inference system to the right way to break apart the WriterT[Id, String, Int].

Community
  • 1
  • 1
Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • Thank you ! All what you said seems very reasonable but I do not understand, I cannot just _simply_ write `f >=> g` for every `f` and `g` that return _any_ monad without all those `Kleisli`, `kleisliU` and everything ... :(( I just still do not get it. – Michael Feb 10 '14 at 19:03
  • 3
    @Michael: "The marvel is not how well the bear dances, but that the bear dances at all." – Travis Brown Feb 10 '14 at 19:06
  • Ok, I see. All this are language limitations. – Michael Feb 10 '14 at 19:16
  • Well, I do not understand why they define `Writer` as `WriterT`. It looks like this causes the compilation problem. – Michael Feb 10 '14 at 19:22
  • 1
    Even `Writer` has two type parameters. You're only going to get type inference for `M[B]` for single-parameter generic types (`Option`, `List`, `IO`, etc.). – Travis Brown Feb 10 '14 at 19:36
  • Aha. Now I am getting it ... That is why the type alias helps here. – Michael Feb 10 '14 at 19:39