Reputation: 6769
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
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