Can't forward infer parameter types

I have once again discovered a Scala “feature” which I had forgotten, (perhaps forgotten and re-learned several times). I’d like to know whether this is considered a feature of a bug in the Scala compiler.

The following definition of every works.

  def every[A,B](L1:List[A],L2:List[B])(f:(A,B)=>Boolean):Boolean = {
    (L1,L2) match {
      case (a::as,b::bs) => f(a,b) && every(as,bs)(f)
      case (_,_) => true
    }
  }
  assert( every(List(10,20,30),List(1,2,3))(_>_))

But neither of the following works, because the compiler cannot figure out the type of _>_.

  def xevery[A,B](f:(A,B)=>Boolean,L1:List[A],L2:List[B]):Boolean = {
    (L1,L2) match {
      case (a::as,b::bs) => f(a,b) && xevery(f,as,bs)
      case (_,_) => true
    }
  }
  assert( xevery(_>_,List(10,20,30),List(1,2,3))) // compiler complains about type of _>_

  def yevery[A,B](f:(A,B)=>Boolean)(L1:List[A],L2:List[B]):Boolean = {
    (L1,L2) match {
      case (a::as,b::bs) => f(a,b) && yevery(f)(as,bs)
      case (_,_) => true
    }
  }
  assert( yevery(_>_)(List(10,20,30),List(1,2,3))) // compiler complains about type of _>_

But I have also discovered that the compiler cannot figure out the type of _>_ in the following case.
This kind of situation probably only ever appears in unit tests anyway.

assert(every(List(),List())(_>_))

I don’t think either “bug” or “feature” is quite right – it’s just a fact of life. The Scala compiler works its way from left to right in the parameter lists. Your every version has the concrete parameters, that provide the type information, first, so it works. But xevery and yevery don’t provide the type information until after you give the function that is trying to use it, so the compiler doesn’t know what to do – it has basically failed before it even gets to the Lists.

Yes, this is sometimes a source of sadness. But it’s a solidly consistent rule: you have to provide sufficient type information to the left before you use it to the right.

As for this case:

assert(every(List(),List())(_>_))

I would guess that it is inferring the Lists as List[Nothing] in this case, and there is no > operator on Nothing.

But yeah – for this case, you should say List.empty[Int] instead of List(), so that the compiler knows the intended contained type. In general, the .empty[] form (which exists for all the standard collections) should be used whenever the compiler doesn’t have enough information to infer the desired type…

1 Like