Reputation: 21
I want below code to be type-checked.
val f: Int => String = x => "a"
val g: (=> Int) => String = x => "b"
def t(h: ???): String = h(42)
t(f)
t(g)
What should be put in "???"? I have read this link and tried it.
t[Int => String, (=> Int) => String].common
res4s: reflect.runtime.universe.TypeTag[=> Int with Int => String] = TypeTag[=> Int with Int => String]
So I put "=> Int with Int => String" in the ???, but t(g) does not type-check. I have tried "(=> Int with Int) => String" but t(f) does not type-check.
So the question is,
What is the meaning of "=> Int with Int => String" and why t(g) does not type-check
What is the meaning of "(=> Int with Int) => String" and why t(f) does not type-check
What should be put in ???
Thanks a lot.
Upvotes: 2
Views: 132
Reputation: 16308
First of all type X => Y
i.e. Function1[X,Y]
is contravariant on type parameter X
so you should search least common subtype, not greatest common supertype of X
and =>X
Second - unfortunately there is not such subtype. More over it's very hard to define anything except function using type =>X
. Although it's known such definition desugars later to Function0[X]
on typecheck level types =>X
and ()=>X
are not equivalent.
Happily we could define complex relations between types for our needs using typeclasses.
As we could use =>X
only in parameter types we could define such a thing:
case class ParamConvertible[X, Y, T](apply: ((Y => T) => X => T))
Which looks and works like some narrow version of contravariant functor
And provide two obvious implementations
implicit def idParamConvertible[X, T] = ParamConvertible((f: X => T) => (x: X) => f(x))
implicit def byNameParamConvertible[X, T] = ParamConvertible((f: (=> X) => T) => (x: X) => f(x))
Now we can generalize your t
function as
def t[T](h: T => String)
(implicit conv: ParamConvertible[Int, T, String]): String =
conv.apply(h)(42)
At this point your
t(f)
t(g)
Should compile and run nicely
Upvotes: 3
Reputation: 13346
The problem is that Int => String
and (=> Int) => String
don't have useful common super type. Int => String
is the type of a call-by-value function which takes an Int
value as first parameter and returns a String
.
In contrast to that (=> Int) => String
is a call-by-name function which takes an expression of type Int
as the first parameter. Every time you access the call-by-name parameter the expression is evaluated. Thus, it is effectively a zero argument function returning an Int
.
What you can do is to convert the call-by-name function into a call-by-value function so that t(h: Int => String): String
type checks also with g
. You simply have to call t(g(_))
.
Upvotes: 1