MaatDeamon
MaatDeamon

Reputation: 9771

Scala weird type when coercing parameterize method into a function?

Can't possibly read the following type return by the REPL

def  returnfuncdefGen[A] = (i: A) => i.toString.length

returnfuncdefGen: [A]=> A => Int

while i do understand that

def  returnfuncdefGen[A](i: A) = i.toString.length

returnfuncdefGen: [A](i: A)Int

The first version is what the author of FP simplified (scala) call coercing parameterize method into a function

How exactly does one read this [A]=> A => Int ?

Also I find the notion of coercion a bit weird. It feels to me like creating a parentheless method that return a function. Now the generic on top i can't explain other by saying that the literal function does a closure of the type defined by the method.

Overall if someone could clarify to me what is happening and how to read that type [A]=> A => Int that would really help.

Upvotes: 2

Views: 147

Answers (3)

Dmytro Mitin
Dmytro Mitin

Reputation: 51683

If you do

object App {
  def returnfuncdefGen[A] = (i: A) => i.toString.length
  def returnfuncdefGen1[A]() = (i: A) => i.toString.length
  def returnfuncdefGen2[A](i: A) = i.toString.length
}

def printSignature(name: String): Unit = {
  import scala.reflect.runtime.universe._
  val t = typeOf[App.type].decl(TermName(name)).typeSignature
  println(s"t=$t, showRaw(t)=${showRaw(t)}")
}

printSignature("returnfuncdefGen")
printSignature("returnfuncdefGen1")
printSignature("returnfuncdefGen2")

you'll see

t=[A]=> A => Int, showRaw(t)=PolyType(List(TypeName("A")), NullaryMethodType(TypeRef(ThisType(scala), scala.Function1, List(TypeRef(NoPrefix, TypeName("A"), List()), TypeRef(ThisType(scala), scala.Int, List())))))
t=[A]()A => Int, showRaw(t)=PolyType(List(TypeName("A")), MethodType(List(), TypeRef(ThisType(scala), scala.Function1, List(TypeRef(NoPrefix, TypeName("A"), List()), TypeRef(ThisType(scala), scala.Int, List())))))
t=[A](i: A)Int, showRaw(t)=PolyType(List(TypeName("A")), MethodType(List(TermName("i")), TypeRef(ThisType(scala), scala.Int, List())))

That's because for NullaryMethodType toString was defined as =>

override def safeToString: String = "=> "+ resultType

Maybe you wanted it to be defined as "" but that was not the case till 2.13.1 (including).

In 2.13.2 for NullaryMethodType toString was changed to ""

override def safeToString: String = resultType.toString

https://github.com/scala/scala/blob/2.13.x/src/reflect/scala/reflect/internal/Types.scala#L2953

So now in 2.13.2, 2.13.3 the above code prints

t=[A]A => Int, showRaw(t)=PolyType(List(TypeName("A")), NullaryMethodType(TypeRef(ThisType(scala), scala.Function1, List(TypeRef(NoPrefix, TypeName("A"), List()), TypeRef(ThisType(scala), scala.Int, List())))))
t=[A](): A => Int, showRaw(t)=PolyType(List(TypeName("A")), MethodType(List(), TypeRef(ThisType(scala), scala.Function1, List(TypeRef(NoPrefix, TypeName("A"), List()), TypeRef(ThisType(scala), scala.Int, List())))))
t=[A](i: A): Int, showRaw(t)=PolyType(List(TypeName("A")), MethodType(List(TermName("i")), TypeRef(ThisType(scala), scala.Int, List())))

as you wanted.

By the way, in Dotty 0.26.0-bin-20200703-2dd1c93-NIGHTLY

def returnfuncdefGen[A] = (i: A) => i.toString.length
def returnfuncdefGen1[A]() = (i: A) => i.toString.length
def returnfuncdefGen2[A](i: A) = i.toString.length
def returnfuncdefGen3 = [A] => (i: A) => i.toString.length
def returnfuncdefGen4() = [A] => (i: A) => i.toString.length

>....def returnfuncdefGen[A] => A => Int
>....def returnfuncdefGen1[A](): A => Int
>....def returnfuncdefGen2[A](i: A): Int
>....def returnfuncdefGen3: PolyFunction{apply: [A](i: A): Int}
>....def returnfuncdefGen4(): PolyFunction{apply: [A](i: A): Int}

Upvotes: 5

Mario Galic
Mario Galic

Reputation: 48420

Until Scala 2.13.1, I believe the notational difference is by design as per SLS 3.3.1 Method Types

A method type is denoted internally as (Ps)𝑈 ... A special case are types of methods without any parameters. They are written here => T.

Because method returnfuncdefGen is without any parameters, then its type is represented using => T notation

scala> def  returnfuncdefGen[A] = (i: A) => i.toString.length
def returnfuncdefGen[A] => A => Int

where T = A => Int

See Dmytro's answer for changes in Scala 2.13.2 by 11416 MethodType.toString prints in scala format #7798

Upvotes: 6

Terry Dactyl
Terry Dactyl

Reputation: 1868

[A] => A => Int is a function with a type parameter of A and no arguments which returns a function `A => Int

This is different to def returnfuncdefGen[A](i: A) = i.toString.length which is a function with a type parameter of A and a single argument of type A which returns an Int

Upvotes: 3

Related Questions