Better type inference for Scala: send us your problematic cases!


@steven-collins-omega The way lift-json construct values by doing implicit conversions everywhere makes it very hard for type inference to figure out what it should be doing, since it can’t rely on the expected type. To avoid adding extra annotations, you could replace List by JList with JList defined a bit like this (I don’t know much about lift-json so no clue if this is a good idea in practice, but it works for your example):

object JList {
  def apply(values: JValue*) = List(values)


@ollijh Because for expressions desugar to map/flatMap/… calls, what you’re asking for would be equivalent to having decode("123").map(x: Int => x) infer intDecoder because the map call happens to only work in that case. This would be impossible to do in general since the result of the implicit search can influence the type of the following .map, and thus the relationship between the type of the argument passed to map and the result type of the decode call. In a nutshell, I’m afraid that because for-expressions are not restricted enough to let us improve type inference in this case, but maybe I’m overlooking something ?


@Lasering As discussed in the linked issue and the linked gitter discussion in the issue, this is doable, but it’s not clear that the extra complexity in the implementation would be worth it since this is such an edge case.


@smarter, do you think this could work in Dotty?

import cats.arrow.Arrow
import cats.implicits._

val f = ((_:Int) + 1) *** ((_:Int) * 2)

(10,20) |> f // ok

(10,20) |> (_ + 1) *** (_ * 2) // missing parameter type for expanded function


Here’s a test case you might want to include: it works in dotty, but not in scalac:


Works as of this PR.


@LPTK I think this is equivalent to:

(x => x)(1)

All I can say without looking deeper into it is that it doesn’t seem impossible, maybe open an issue so we keep track of that ?

@lrytz PRs adding test cases are more than welcome!


@smarter no it’s not. The following works well in all versions of Scala:

implicit class Helper[A](self: A) {
  def |> [B] (rhs: A => B): B = rhs(self)
1 |> (x => x)


Ah indeed, can you rewrite it without the cats dependency to make things simpler to analyze ?


Here’s a bizarre implicit resolution problem that I once found:

trait Typeclass[T]
object Typeclass {
  implicit def seqTC[C[X] <: Seq[X], T: Typeclass]: Typeclass[C[T]] = ???
  implicit def setTC[C[X] <: Set[X], T: Typeclass]: Typeclass[C[T]] = ???

class Stuff[T]
object Stuff {
  implicit val stuffTC: Typeclass[Stuff[_]] = ???

object Test {
  implicitly[Typeclass[Stuff[_]]] // error!

The result is:

error: diverging implicit expansion for type Typeclass[Stuff[_]]
starting with method setTC in object Typeclass

Workaround for this is to add redundant with Seq/Set[T] to the result types of implicits:

implicit def seqTC[C[X] <: Seq[X], T: Typeclass]: Typeclass[C[T] with Seq[T]] = ???
implicit def setTC[C[X] <: Set[X], T: Typeclass]: Typeclass[C[T] with Set[T]] = ???


@ghik Already works in Dotty!