seeming parsing inconsistency with toArray, ClassTag, and indexing array

94 views
Skip to first unread message

Daniel Barclay

unread,
Jul 21, 2016, 1:07:25 PM7/21/16
to scala-user
In this this code:

val x1: List[Int] = List()

val x2 = x1.toArray
val x3 = x2(0)

val x4 = x1.toArray(0)

val x5 = ( x1.toArray )(0)

why do the lines with x4 and x5, especially x5 line, fail to
compile (with Scala 2.10), yield the following errors?:

[ERROR] ...: error: type mismatch;
[INFO] found : Int(0)
[INFO] required: scala.reflect.ClassTag[?]
[INFO] val x4: Int = x1.toArray(0)
[INFO] ^
[ERROR] ...: error: type mismatch;
[INFO] found : Int(0)
[INFO] required: scala.reflect.ClassTag[?]
[INFO] val x5: Int = ( x1.toArray )(0)
[INFO] ^

The x4 case isn't so surprising. I guess that the "(0)" is taken as
the argument list for toArray's (implicit) parameter list (defined by
the ": ClassTag" in "def toArray[B >: A : ClassTag]: Array[B]"), rather
than being taken as argument list for the array returned by toArray.

However, x5 is surprising. I would think that the parentheses
surrounding "x1.toArray" would isolate it syntactically from the "(0)",
causing it to use an implicit arguments list for its parameter list,
leaving the "(0)" to be associated with the returned Array (and index

it).


So, what exactly is going on?


Thanks,
Daniel


satorg

unread,
Jul 21, 2016, 11:44:45 PM7/21/16
to scala-user
I would think that the parentheses surrounding "x1.toArray" would isolate it syntactically from the "(0)" ...

It seems it should but actually it doesn't.
I wonder why scala compiler works in this way, but if you need to write it in one line the only work-around I know is

x1.toArray.apply(0)

Oliver Ruebenacker

unread,
Jul 22, 2016, 7:42:06 AM7/22/16
to satorg, scala-user

     Hello,

  I suppose the method is converted to a function, and being surrounded by parenthesis, a function is still a function.

  Compare:

scala> def m(i: Int): Int = 2*i
m: (i: Int)Int

scala> val f: Int => Int = m
f: Int => Int = <function1>

scala> f(1)
res1: Int = 2

  Best, Oliver

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Oliver Ruebenacker
Senior Software Engineer, Diabetes Portal, Broad Institute

Daniel Barclay

unread,
Jul 22, 2016, 10:45:39 AM7/22/16
to satorg, scala-user
satorg wrote:
I would think that the parentheses surrounding "x1.toArray" would isolate it syntactically from the "(0)" ...

It seems it should but actually it doesn't.
I wonder why scala compiler works in this way, but if you need to write it in one line the only work-around I know is

x1.toArray.apply(0)

Thanks--I forgot about making the call to apply(...) explicit.

Daniel


Daniel Barclay

unread,
Jul 22, 2016, 10:48:07 AM7/22/16
to Oliver Ruebenacker, scala-user
Oliver Ruebenacker wrote:

     Hello,

  I suppose the method is converted to a function,
Why do you think/suppose that the compiler is creating a function object that calls toArray?

Daniel

Oliver Ruebenacker

unread,
Jul 22, 2016, 5:27:51 PM7/22/16
to Daniel Barclay, scala-user

     Hello,

  Because it would explain why extra parentheses don't make a difference.

     Best, Oliver

Seth Tisue

unread,
Aug 2, 2016, 6:47:16 PM8/2/16
to scala-user
On Friday, July 22, 2016 at 2:27:51 PM UTC-7, Oliver Ruebenacker wrote:

  Because it would explain why extra parentheses don't make a difference.

Yeah, but it isn’t actually a correct explanation, as we can verify as follows:


scala> import scala.reflect.runtime.{universe => u}

import scala.reflect.runtime.{universe=>u}


scala> def foo(x: Int)(y: String) = y * x

foo: (x: Int)(y: String)String


scala> u.reify { (foo(3))("bar") }

res5: reflect.runtime.universe.Expr[String] = Expr[String]($read.foo(3)("bar"))


scala> u.reify { foo(3)("bar") }

res6: reflect.runtime.universe.Expr[String] = Expr[String]($read.foo(3)("bar"))


The resulting AST is the same regardless of whether the parentheses are present or not.


This is what we ought to expect anyway, since eta expansion doesn't take place unless a function type is expected, but there's no reason here for a function type to be expected. So eta expansion only happens if we explicitly ask for it:


scala> (foo(3) _)("bar")

res7: String = barbarbar


We can also arrive at the same conclusion by inspecting the generated bytecode:


scala> object O { (foo(3))("bar") }

defined object O


scala> :javap O$

         0: aload_0

         1: invokespecial #13                 // Method java/lang/Object."<init>":()V

         4: aload_0

         5: putstatic     #15                 // Field MODULE$:LO$;

         8: getstatic     #20                 // Field .MODULE$:L;

        11: iconst_3

        12: ldc           #22                 // String bar

        14: invokevirtual #26                 // Method .foo:(ILjava/lang/String;)Ljava/lang/String;

        17: pop

        18: return

...


where we see that this compiles down to an ordinary method call, rather than going through an apply method on a function object, as happens if we explicitly eta-expand:

scala> object O { (foo(3) _)("bar") }
defined object O

scala> :javap O$
...
         0: aload_0
         1: invokespecial #43                 // Method java/lang/Object."<init>":()V
         4: aload_0
         5: putstatic     #45                 // Field MODULE$:L$line4/$read$$iw$$iw$O$;
         8: invokedynamic #67,  0             // InvokeDynamic #0:apply:()Lscala/Function1;
        13: ldc           #69                 // String bar
        15: invokeinterface #73,  2           // InterfaceMethod scala/Function1.apply:(Ljava/lang/Object;)Ljava/lang/Object;
        20: pop
        21: return
...

In SLS 3.3.1, method types are defined recursively: a method type's result type can be another method type. That's how multiple parameter lists are represented. Then in SLS 6.6 the syntax production for SimpleExpr that handles method calls only handles one parameter list at a time. And a SimpleExpr may be surrounded by any amount of parentheses; its type can still be a method type, so that's why a(b)(c) and (a(b))(c) parse the same.

Oliver, your intuition is correct that `a(b)(c)` is parsed first by parsing `a(b)`, then by parsing the additional `(c)`, rather than parsing both argument lists together. But the type of `a(b)` is a method type, not a function value type.

Seth Tisue / Scala team / Lightbend, Inc.

Seth Tisue

unread,
Aug 2, 2016, 8:50:15 PM8/2/16
to scala-user
P.S. note this comes up a lot; see e.g. most recently https://issues.scala-lang.org/browse/SI-9862 . I just recorded some additional thoughts there about why I think it works that way.
Reply all
Reply to author
Forward
0 new messages