ixaxaar
ixaxaar

Reputation: 6769

Scala type inference does not work with type bounds, unless the type is specified explicitly

I'm trying to create a documentation wrapper around functions in scala like, so that the wrapper can be queried for its containing function's documentation, like so:

trait WrappedFunction1[A, B] {
  def f : Function1[A, B]
  def doc: String
  def apply(x:A):B = f(x)
  def compose[C, T <:WrappedFunction1[B, C]](that:T):WrappedFunction1[A, C] = 
    new Wrapper[A, C](this.f andThen that.f, this.doc + " composed with " + that.doc)
}

class Wrapper[A, B](f1:Function1[A, B], sos:String) extends WrappedFunction1[A, B] {
  val f = f1
  val doc = sos
}

object Wrapper {
  implicit class Wrap[A, B](f1:Function1[A, B]) {
    def wrap(sos:String):WrappedFunction1[A, B] = new Wrapper(f1, sos)
  }
}

Here's how I'd use this:

import Wrapper._
val x : String => String = _.toLowerCase
val y : String => String = _.toUpperCase
val x1 = x.wrap("a function for lowercasing")
val y1 = y.wrap("a function for uppercasing")

println(x1("LOL")) // lol
println(x1.doc) // a function for lowercasing

However, I cant get the type inferred when I compose two of these:

val xy = x1 compose y1

cmd3.sc:1: inferred type arguments [Nothing,cmd3Wrapper.this.cmd1.cmd0.wrapper.WrappedFunction1[String,String]] do not conform to method compose's type parameter bounds [C,T <: cmd3Wrapper.this.cmd1.cmd0.wrapper.WrappedFunction1[String,C]]
val xy = x1 compose y1
            ^cmd3.sc:1: type mismatch;
 found   : cmd3Wrapper.this.cmd1.cmd0.wrapper.WrappedFunction1[String,String]
 required: T
val xy = x1 compose y1
                    ^
Compilation Failed

Composing them works if I explicitly state the types:

val xy = x1 compose[String, WrappedFunction1[String, String]] y1

Is there somewhere I'm going wrong? Also is there a better way to do this? (I tried typeclasses but they seem to be defined for traits with one type param, any other algebraic datatype perhaps?)

Upvotes: 0

Views: 99

Answers (1)

Alexey Romanov
Alexey Romanov

Reputation: 170919

The problem is in details of Scala's type inference. It can't infer T first and then infer C from it; instead it has to infer both at once.

From that: T it can determine T, but C isn't mentioned in parameter types, so it's assigned Nothing and only on the next step the compiler notices it doesn't fit. So the fix is to change the type to

def compose[C, T <:WrappedFunction1[B, C]](that: T with WrappedFunction1[B, C])

Or better, simply

def compose[C](that: WrappedFunction1[B, C])

because this already allows to pass any subtype of WrappedFunction1[B, C]!

Upvotes: 1

Related Questions