-
Notifications
You must be signed in to change notification settings - Fork 19
Inferring List[Any] #16
Comments
I'm uncomfortable with the original suggestion of never inferring Any, because it's irregular, and most Scala irregularities are agreed to be bugs. I'd need to try out a compiler like that before agreeing the idea is good. What should be the result type of this list? Is it intended to be List[X], or is there a bug?
Similar examples can be repeated at various levels of the hierarchy. You can try to guess what the programmer meant by letting the list members "vote" (so that in So in general, you will need some annotations at some point. This problem arises also because ADTs are encoded by reusing subtyping. So a cleaner design to avoid this problem is to avoid this conflation altogether: ML-like languages have subtyping, but limit it to the module system. I imagine Robert Harper would point this out. I remember also @pchiusano disliked subtyping — is this an argument against subtyping that you like as well, @pchiusano? I think this difficulty is related with this quote from PFPL:
I agree subtyping is annoying here; but limiting subtyping does have its downsides (and it wouldn't be Scala). So maybe we have to take a non-clean solution for convenience here. But I can't help hoping for some cooler idea here. Still, a warning for inferred |
You're late to this party:
|
I'd love Also as has been pointed out, not inferring |
Any is just the most visible, yes, but the list doesn't go on forever. What good is it to infer Serializable? After Serializable and Product, which we can view as artifacts of the implementation, what type is going to be the lub with any frequency? |
Or maybe you're in the club of AnyVal lovers. Not sure that's a real club.
|
Well, if you don't infer Serializable and Product, you've eliminated some bad cases but more will occur in user-defined type hierarchies (although at least users have control over this to some degree) To solve it in a user-defined manner, you might need something like "top limit" and "bottom limit" annotations that prevent type inference from inferring a less specific type than something annotated with "top limit", or a more specific type than something annotated with "bottom limit". |
@jdegoes, I love that! We can add syntactic sugar on top, but adding that annotation sounds like a great improvement. You still need to guess where to put it, and some occurrences are in the standard library, but hey, a library is of course entitled to have ad-hoc code — that's much better than the compiler. And users could add that flag. Personally, I'm happy with @NonInferrable on all the examples Paul mentioned (Any, AnyVal, Serializable, Product). Per #14, this flag would have to be honored under some
I'm not sure yet that's an improvement. Some examples where you need these more complex annotations? |
We should be able to add just an annotation without requiring a flag. And yes, I really want a NonInferrable annotation. |
Bottom limit is equivalent in power to e.g. sealed trait Node
@InferrableTop
sealed trait Decl extends Node
@NonInferrable
final case class FnDecl(...) extends Node
@InferrableTop
sealed trait Expr extends Node
@NonInferrable
final case class Mul(...) extends Expr Thus, things like, |
SI-8345 support both scalap 2.11.0-M8 and 2.11.0-RC1
I know it’s probably not merge-compatible or fully existing, but what happened to this @paulp? https://groups.google.com/d/topic/scala-language/J8LpYDmrOCg/discussion val x = List(1, "X", 2.0)
x: List[Int|String|Double] = List(1, X, 2.0) |
No idea. I burned hundreds of branches. My comments in that thread are pretty good I think, and they will have to serve as my legacy. |
In Scala 2.8.2, an optimization was added to create a static cache for Symbol literals (ie, the results of `Symbol.apply("foo"))`. This saves the map lookup on the second pass through code. This actually was broken somewhere during the Scala 2.10 series, after the addition of an overloaded `apply` method to `Symbol`. The cache synthesis code was made aware of the overload and brought back to working condition recently, in scala#3149. However, this has uncovered a latent bug when the Symbol literals are defined with traits. One of the enclosed tests failed with: jvm > t8933b-run.log java.lang.IllegalAccessError: tried to access field MotherClass.symbol$1 from class MixinWithSymbol$class at MixinWithSymbol$class.symbolFromTrait(A.scala:3) at MotherClass.symbolFromTrait(Test.scala:1) This commit simply disables the optimization if we are in a trait. Alternative fixes might be: a) make the static Symbol cache field public / b) "mixin" the static symbol cache. Neither of these seem worth the effort and risk for an already fairly situational optimization. Here's how the optimization looks in a class: % cat sandbox/test.scala; qscalac sandbox/test.scala && echo ":javap C" | qscala; class C { 'a; 'b } Welcome to Scala version 2.11.5-20141106-145558-aa558dce6d (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_20). Type in expressions to have them evaluated. Type :help for more information. scala> :javap C Size 722 bytes MD5 checksum 6bb00189166917254e8d40499ee7c887 Compiled from "test.scala" public class C { public static {}; descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=0, args_size=0 0: getstatic typelevel#16 // Field scala/Symbol$.MODULE$:Lscala/Symbol$; 3: ldc typelevel#18 // String a 5: invokevirtual typelevel#22 // Method scala/Symbol$.apply:(Ljava/lang/String;)Lscala/Symbol; 8: putstatic typelevel#26 // Field symbol$1:Lscala/Symbol; 11: getstatic typelevel#16 // Field scala/Symbol$.MODULE$:Lscala/Symbol$; 14: ldc typelevel#28 // String b 16: invokevirtual typelevel#22 // Method scala/Symbol$.apply:(Ljava/lang/String;)Lscala/Symbol; 19: putstatic typelevel#31 // Field symbol$2:Lscala/Symbol; 22: return public C(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial typelevel#34 // Method java/lang/Object."<init>":()V 4: getstatic typelevel#26 // Field symbol$1:Lscala/Symbol; 7: pop 8: getstatic typelevel#31 // Field symbol$2:Lscala/Symbol; 11: pop 12: return } fixup
Rather than in implementation of the abstract method in the expanded anonymous class. This leads to more more efficient use of the constant pool, code shapes more amenable to SAM inlining, and is compatible with the old behaviour of `-Xexperimental` in Scala 2.11, which ScalaJS now relies upon. Manual test: ``` scala> :paste -raw // Entering paste mode (ctrl-D to finish) package p1; trait T { val x = 0; def apply(): Any }; class DelambdafyInline { def t: T = (() => "") } // Exiting paste mode, now interpreting. scala> :javap -c p1.DelambdafyInline Compiled from "<pastie>" public class p1.DelambdafyInline { public p1.T t(); Code: 0: new scala#10 // class p1/DelambdafyInline$$anonfun$t$1 3: dup 4: aload_0 5: invokespecial scala#16 // Method p1/DelambdafyInline$$anonfun$t$1."<init>":(Lp1/DelambdafyInline;)V 8: areturn public final java.lang.Object p1$DelambdafyInline$$$anonfun$1(); Code: 0: ldc scala#22 // String 2: areturn public p1.DelambdafyInline(); Code: 0: aload_0 1: invokespecial scala#25 // Method java/lang/Object."<init>":()V 4: return } scala> :javap -c p1.DelambdafyInline$$anonfun$t$1 Compiled from "<pastie>" public final class p1.DelambdafyInline$$anonfun$t$1 implements p1.T,scala.Serializable { public static final long serialVersionUID; public int x(); Code: 0: aload_0 1: getfield scala#25 // Field x:I 4: ireturn public void p1$T$_setter_$x_$eq(int); Code: 0: aload_0 1: iload_1 2: putfield scala#25 // Field x:I 5: return public final java.lang.Object apply(); Code: 0: aload_0 1: getfield scala#34 // Field $outer:Lp1/DelambdafyInline; 4: invokevirtual scala#37 // Method p1/DelambdafyInline.p1$DelambdafyInline$$$anonfun$1:()Ljava/lang/Object; 7: areturn public p1.DelambdafyInline$$anonfun$t$1(p1.DelambdafyInline); Code: 0: aload_1 1: ifnonnull 6 4: aconst_null 5: athrow 6: aload_0 7: aload_1 8: putfield scala#34 // Field $outer:Lp1/DelambdafyInline; 11: aload_0 12: invokespecial scala#42 // Method java/lang/Object."<init>":()V 15: aload_0 16: invokespecial scala#45 // Method p1/T.$init$:()V 19: return } scala> :quit ``` Adriaan is to `git blame` for `reflection-mem-typecheck.scala`.
While talking about a
List(1, "X", 2.0)
which was inferred to be List[Any] but was a bug, @paulp wrote in #1:I see why here (and probably elsewhere) inferring
List[Any]
is inconvenient.We should certainly consider a warning for this, and even @paulp's idea of not inferring Any: this is what this issue should be about.
The text was updated successfully, but these errors were encountered: