2

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.

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
MaatDeamon
  • 9,532
  • 9
  • 60
  • 127
  • 2
    I don’t think this is coercion. It’s simply a method that returns a function – user Jul 06 '20 at 13:00
  • 1
    I guess the question refers to this page https://alvinalexander.com/scala/fp-book-diffs-val-def-scala-functions/ (section "Coerce a parameterized method into a function") – Dmytro Mitin Jul 06 '20 at 13:07
  • 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 https://en.wikipedia.org/wiki/Lambda_calculus#%CE%B7-reduction – Dmytro Mitin Jul 06 '20 at 13:16
  • 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 Jul 06 '20 at 13:17
  • But ethod return type notation never uses arrow. Why is it that in this case it does ``` [A] => .....``` usual is it is either parentheis or simmply nothing and just the return type. Why not ```[A] Int => Int ?``` – MaatDeamon Jul 06 '20 at 13:22
  • which is exactly what is being returned. Example ```def returnfuncdef = (i: int) => i.toString.length``` this gives me ```returnfuncdef: Int => Int``` in the REALP – MaatDeamon Jul 06 '20 at 13:30
  • If i do ```def returnfuncdefGen[A](i: A) = i.toString.length``` this gives me ```returnfuncdefGen: [A](i: A)Int``` – MaatDeamon Jul 06 '20 at 13:32
  • So do you understand the issue with the notation now ? method do not use arrow for their return type. – MaatDeamon Jul 06 '20 at 13:33
  • 1
    I think I see what you mean. You want it to be [A](A) => Int? – user Jul 06 '20 at 13:40
  • Yup that’s right. It is just consistent with the rest that way – MaatDeamon Jul 06 '20 at 13:48
  • @MaatDeamon Actually starting from 2.13.2 it prints as you want. See details in my answer. – Dmytro Mitin Jul 06 '20 at 14:19
  • Ah nice, many thanks. Still on 2.12 :) – MaatDeamon Jul 06 '20 at 14:21

3 Answers3

6

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

Mario Galic
  • 47,285
  • 6
  • 56
  • 98
5

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}
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
3

[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

Terry Dactyl
  • 1,839
  • 12
  • 21
  • Taking your first paragraph would mean that there is a coercion happening ? Def is coerced into a function ? Otherwise why is it not a method of type a with no argument that return a function ? – MaatDeamon Jul 06 '20 at 12:49
  • This is so strange because ```def returnfuncdef = (i: Int) => i + 2``` does not coerce anything ? Does it ? the type here is Int => Int. Which to me is a method that is parentheless and return a function. – MaatDeamon Jul 06 '20 at 12:54
  • 1
    *"`[A] => A => Int` is a function ..."*, *"`def returnfuncdefGen[A](i: A) = i.toString.length` which is a function ..."* More precisely, not a function but method. – Dmytro Mitin Jul 06 '20 at 13:05
  • Ok got it. I guess the typing for that is a bit inconsistent with the how method or function are usually type in the`` REALP: [A] => .....`` is strange, it does not follow the typical method typing notation which do not use ```=>``` – MaatDeamon Jul 06 '20 at 13:16
  • 2
    Neither are functions, both are methods. But the first method returns a function. The differences between functions and methods are very important, specially for newcomers. See [this](https://stackoverflow.com/questions/2529184/difference-between-method-and-function-in-scala) for a detailed explanation of the differences. – Luis Miguel Mejía Suárez Jul 06 '20 at 13:18
  • 1
    @LuisMiguelMejíaSuárez yes fully agree, the only issue is the notation. usually method do not use arrow for their return type. But in this case it does. The function returned is ```A => Int``` but then you have ```[A] => A => Int```, in other words, ```[A] => ```` is about the method and that is where my issue is. I agree, i just think the notation is wrong. – MaatDeamon Jul 06 '20 at 13:37