简体   繁体   中英

Scala supertype of call-by-name and call-by-value

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,

  1. What is the meaning of "=> Int with Int => String" and why t(g) does not type-check

  2. What is the meaning of "(=> Int with Int) => String" and why t(f) does not type-check

  3. What should be put in ???

Thanks a lot.

First of all type X => Y ie 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

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(_)) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM