2
votes

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.

3
I don’t think this is coercion. It’s simply a method that returns a function - user
I guess the question refers to this page alvinalexander.com/scala/fp-book-diffs-val-def-scala-functions (section "Coerce a parameterized method into a function") - Dmytro Mitin
I guess in Scala language specification there is no notion "coercion" (although there are coercions for example in Haskell). Transformation of def returnfuncdefGen[A](i: A) = i.toString.length into def returnfuncdefGen[A] = (i: A) => i.toString.length is similar to η-reduction en.wikipedia.org/wiki/Lambda_calculus#%CE%B7-reduction - Dmytro Mitin
Fat arrows are right associative (I think), so you can imagine parens around A => Int. Also, adding an empty parameter list to the first Method may help you - user
I think I see what you mean. You want it to be [A](A) => Int? - user

3 Answers

6
votes

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

5
votes

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}
3
votes

[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