Cats: contramap on Function1 broke between 0.9 to 1.0.0-MF

Created on 24 Aug 2017  路  6Comments  路  Source: typelevel/cats

In cats 0.9 you could call .contramap on a Function1[A,R]. In 1.0.0-MF it no longer works. @sellout hypothesised that there might have been an explicit method on Function1Ops.

Discussion happened here: https://gitter.im/typelevel/cats?at=599dfc7e19147ac32315dcc7

bug

Most helpful comment

I'd say 1. is our best option here. Unapply has a huge cost in compilation time and doesn't scale to different arities. In general, the story for type classes in multiple type parameters is bad, in every functional programming language I know of. Afaik the best solution for now is to just need a type class which combines every type class for every type parameter of the type constructor, like in this case Profunctor, specifically using lmap.

All 6 comments

In 0.9.0 it works due to Unapply, in particular Unapply.catsUnapply2left (note the _left_).

For those wondering how @durban figured out that it worked with Unapply.catsUnapply2left, here's one way he could have done that: :-)

scala> scala.reflect.runtime.universe.reify {
  ((_: Int).toString).contramap((_: Int) * 2)
}

res0: reflect.runtime.universe.Expr[Int => String] = Expr[Int => java.lang.String](
  `package`.contravariant.catsSyntaxUContravariant(((x$1: Int) => (x$1: Int).toString()))(
    Unapply.catsUnapply2left(`package`.function.catsStdContravariantForFunction1)
  ).contramap(((x$2: Int) => (x$2: Int).$times(2)))
)

Yes, that's a useful thing to know; although I've used -Xprint:typer :smile:

This is due to the fact that the Contravariant syntax no longer works on Function1[?, B] under the partial unification SI-2712 fix ( a replacement for the removed Unapply mechanism)
The partial unification fix has one limitation over Unapply, it only support left-to-right partial order. In the Contravariant of Function1[?, B] case, the left-to-right order no longer applies.

The immediate work around is to use the Contravariant type class directly instead of the syntax. I.e. the following works

Contravariant[? => B].contramap(f)(...)

Update: as suggested by @edmundnoble below lmap from Profunctor is a better alternative.

I see a couple of options ahead.

  1. document this limitation of partial unification in cats type class syntax and have our users use the work around above.
  2. bring Unapply back for this kind of right to left unification scenarios (anything else other than this one)?

What do you guys think?

I'd say 1. is our best option here. Unapply has a huge cost in compilation time and doesn't scale to different arities. In general, the story for type classes in multiple type parameters is bad, in every functional programming language I know of. Afaik the best solution for now is to just need a type class which combines every type class for every type parameter of the type constructor, like in this case Profunctor, specifically using lmap.

sort of fixed by #1937 , i.e. we are not going to make any change to the code, instead @LukaJCB provided a scalafix to help people migrate to lmap which has better type inference.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kailuowang picture kailuowang  路  3Comments

davidabrahams picture davidabrahams  路  3Comments

adelbertc picture adelbertc  路  5Comments

non picture non  路  3Comments

Atry picture Atry  路  5Comments