should Kleisli contravariant on A? #2749

Closed
opened this issue Mar 10, 2019 · 0 comments

Projects
None yet
Contributor

jcouyang commented Mar 10, 2019 • edited

Kleisli[F[_], A, B] is supposed to be abstraction of `A => F[B]`, but `=>` has type of Function1[-A, +B], I can't figure out why Kleisli is invariant on both A and B, is there any benefit that it's design like this?

from what I observed, it will be much easier to let scala compiler find out what A should be

for instance, if I would like to choose from two Kleisli

contravariant on -A

```case class Kleisli[F[_], -A, B](run: A => F[B])

def first[A, B, C, F[_]](a: Kleisli[F, A, B], b: Kleisli[F, A, C]) = a

trait A1
trait A2
trait A12 extends A1 with A2

first(Kleisli((a: A1) => Some(a)), Kleisli((b:A2)=>Some(b)))
res7: Kleisli[Some, A2 with A1, A1] =  ...```

compiler can infer the correct type of the input of Kleisli should be `A2 with A1` and the output will be `A1`

to achieve the same thing without `-A` will be much difficult

by contramap

```def first[A1, A2, A12, B, C, F[_]](a: Kleisli[F, A1, B], b: Kleisli[F, A2, C])
(implicit ev: A12<:<A1):Kleisli[F, A12, B] =
Contravariant[Kleisli[F, ?,B]].contramap(a)(ev)```
```@ first(Kleisli((a: A1) => Some(a)), Kleisli((b:A2)=>Some(b)))
cmd14.sc:1: type mismatch;
found   : Nothing <:< Nothing
required: A12 <:< ammonite.\$sess.cmd9.A1
val res14 = first(Kleisli((a: A1) => Some(a)), Kleisli((b:A2)=>Some(b)))
^```

the compiler can't infer A12 for contramap unless I explicitly tell the compiler that I need `Kleisli[Some, A12, A1]`

```@ first(Kleisli((a: A1) => Some(a)), Kleisli((b:A2)=>Some(b))) : Kleisli[Some, A12, A1]
res14: Kleisli[Some, A12, A1] = Kleisli(scala.Function1\$\$Lambda\$35/695682681@1e3c4c12)```

Merged