i don't get the :javap output here -- can someone tell me in the following, Test is fully specialized? my gutts say it can't because of the variance annotation of type A in trait KeyObserver -- but then shouldn't scalac complain about this?
```scala
trait KeyObserver[ @specialized( Int, Long ) -A ] { def keyUp( key: A ) : Unit }
object NoKeyObserver extends KeyObserver[ Any ] { def keyUp( key: Any ) {} }
defined module NoKeyObserver
trait Test[ @specialized( Int, Long ) A ] {
def lala : A
def obs : KeyObserver[ A ]
// test specialization
obs.keyUp( lala )
}
should i change this to
```scala
trait KeyObserver[ @specialized( Int, Long ) A ] { def keyUp( key: A ) : Unit }
def NoKeyObserver[ A ] : KeyObserver[ A ] = new KeyObserver[ A ] { def keyUp( key: A ) {} }
instead?
thanks, -sciss-
trait KeyObserver[ @specialized( Int ) A ] { def keyUp( k: A ) : Unit }
class Test2[ @specialized( Int ) A ]( obs: KeyObserver[ A ], lala: A ) { obs.keyUp( lala )}
scala> :javap -v Test2
public Test2(KeyObserver, java.lang.Object);
Code:
Stack=2, Locals=3, Args_size=3
0: aload_0
1: aload_1
2: putfield #17; //Field obs:LKeyObserver;
5: aload_0
6: aload_2
7: putfield #19; //Field lala:Ljava/lang/Object;
10: aload_0
11: invokespecial #24; //Method java/lang/Object."<init>":()V
14: aload_0
15: getfield #17; //Field obs:LKeyObserver;
18: aload_0
19: getfield #19; //Field lala:Ljava/lang/Object;
22: invokeinterface #30, 2; //InterfaceMethod KeyObserver.keyUp:(Ljava/lang/Object;)V // !!
27: return
i can't believe it.
so what is the purpose of specialization -- do i need to write for every primitive still concrete classes, and i must under no circumstances call into generic methods?
something must be wrong here, i hope?
best, -sciss-
trait Key[ @specialized( Int ) A ] { def up( v: A ) : Unit }
class KeyTest[ @specialized( Int ) A ] extends Key[ A ] { def up( v: A ) { sys.error( "here" )}}
final case class Value[ @specialized( Int ) A ]( v: A )
trait Test[ @specialized( Int ) A ] {
def value: Value[ A ]
def key: Key[ A ]
def test { key.up( value.v )}
}
object TestFactory {
def apply[ @specialized( Int ) A ]( _key: Key[ A ], _value: Value[ A ]) : Test[ A ] = new Test[ A ] { def key = _key; def value = _value }
}
val x = TestFactory( new KeyTest[ Int ], Value( 33 ))
x.test
java.lang.RuntimeException: here
at scala.sys.package$.error(package.scala:27)
at KeyTest$mcI$sp.up$mcI$sp(<console>:8)
at KeyTest$mcI$sp.up(<console>:8)
at KeyTest$mcI$sp.up(<console>:8)
at Test$class.test(<console>:13)
at TestFactory$$anon$2.test(<console>:12)
at .<init>(<console>:15)
at .<clinit>(<console>)
at .<init>(<console>:11)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:704)
at scala.tools.nsc.interpreter.IMain$Request$$anonfun$14.apply(IMain.scala:920)
at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:43)
at scala.tools.nsc.io.package$$anon$2.run(package.scala:25)
at java.lang.Thread.run(Thread.java:637)
so can it be that there is a limitation for the constructor body of specialized traits? otherwise this seems to compose nicely and as expected.... however i don't see in the stack trace that actually a specialized version of method `test` has been called...?
best, -sciss-
oh.... so it did _not_ work?
here is another example that i thought should work:
final case class Compare[ @specialized( Int ) A ]( fun: (A, A) => Int ) { def apply( a: A, b: A ) : Int = fun( a, b )}
trait SkipList[ @specialized( Int ) A ] {
def compareFun: Compare[ A ]
private class Node( var value: A ) {
def compare( b: Node ) : Int = compareFun( value, b.value )
}
def test( a: A, b: A ) : Int = {
val n = new Node( a )
val m = new Node( b )
n compare m
}
}
object SkipList { def apply[ A ]( _compareFun: Compare[ A ]) : SkipList[ A ] = new SkipList[ A ] { def compareFun = _compareFun }}
val l = SkipList( Compare[ Int ] { (a, b) => sys.error( "here" )})
l.test( 1, 2 )
java.lang.RuntimeException: here
at scala.sys.package$.error(package.scala:27)
at $anonfun$1.apply(<console>:11)
at $anonfun$1.apply(<console>:11)
at scala.Function2$class.apply$mcIII$sp(Function2.scala:33)
at scala.runtime.AbstractFunction2.apply$mcIII$sp(AbstractFunction2.scala:12)
at Compare$mcI$sp.apply$mcI$sp(<console>:7)
at Compare$mcI$sp.apply(<console>:7)
at Compare$mcI$sp.apply(<console>:7)
at SkipList$Node.compare(<console>:12)
at SkipList$class.test(<console>:18)
at SkipList$$anon$1.test(<console>:10)
at SkipList$class.test$mcI$sp(<console>:15)
at SkipList$$anon$1.test$mcI$sp(<console>:10)
at .<init>(<console>:13)
...
again here is your pattern:
at Compare$mcI$sp.apply$mcI$sp(<console>:7)
at Compare$mcI$sp.apply(<console>:7)
at Compare$mcI$sp.apply(<console>:7)
however not in the actual function call (scala.runtime.AbstractFunction2.apply$mcIII$sp)
--- so i don't know what to make of this. is there boxing happing?
if so, why is that? because Node.compare doesn't contain a "naked" A? that would truly suck. should i add a dummy argument? (that would be terribly messy)
thanks again, -sciss-
On 7 Nov 2011, at 23:04, Aleksey Nikiforov wrote:
> Seem you have posted more while I was typing the response. When you see something like this in the stack trace, it means specialization did not work out:
>
> at KeyTest$mcI$sp.up$mcI$sp(<console>:8)
> at KeyTest$mcI$sp.up(<console>:8)
> at KeyTest$mcI$sp.up(<console>:8)
>
> When specialization works you will see only one specialized call:
>
> at KeyTest$mcI$sp.up$mcI$sp(<console>:8)
>
>
trait X[ @specialized( Int ) A ] {
private class Node {
var payload: A
}
}
i don't get why that wouldn't be specialized. this is by no means creating any sort of explosion, because Node as an inner class is inherently tied to X, so the maximum number of classes equals the number of specializations in A, not to the power of two.
this just shows for me that type parameters and path-dependent types are second-class citizens in scala, and i find that very implausible.
best, -sciss-
trait X {
type Y
}
versus
trait X[ Y ]
trait SkipList[ @specialized( Int, Long) A ] {
@specialized private class Node {
var value A
...
}
}
the idea of specialization for me is to get maximum performance, that's the only reason why you do the effort to put the annotations. so if get a half-way specialized result with numerous boxing, unboxing, that is clearly unintended and counter-intuitive. the compiler should at least tell me
Warning: inner class Node is not specialized in A.
enforce specialization by annotating this class as @specialized
or similar
?
The current behaviour just means Scala is unsuitable for creating elegant high-performance data structures. When i start now to move around my classes and annotate methods and so forth to eventually end up in a fully specialized data structure -- which seems almost impossible to observe --, probably the result would be more readable if i go back to Java. I'm very disappointed by this.
best, -sciss-
The current behaviour just means Scala is unsuitable for creating elegant high-performance data structures.
When i start now to move around my classes and annotate methods and so forth to eventually end up in a fully specialized data structure -- which seems almost impossible to observe --, probably the result would be more readable if i go back to Java. I'm very disappointed by this.
best, -sciss-
The current behaviour just means Scala is unsuitable for creating elegant high-performance data structures. When i start now to move around my classes and annotate methods and so forth to eventually end up in a fully specialized data structure -- which seems almost impossible to observe --, probably the result would be more readable if i go back to Java. I'm very disappointed by this.
but that doesn't really scale, because once you need to change code afterwards, you need to change it three times.
i wonder if there is maybe a capable source-rewriting tool around that can do this refactoring automatically, like a nice little sbt plugin... ?
best, -sciss-
best, -sciss-
[info] Compiling 1 Scala source to /Users/hhrutz/Documents/devel/TreeTests/target/scala-2.9.1/classes...
[error] {file:/Users/hhrutz/Documents/devel/TreeTests/}default-92482a/compile:compile: scala.tools.nsc.symtab.Types$TypeError: type mismatch;
[error] found : de.sciss.collection.txn.HASkipList.Node[S(in class Leaf),A]
[error] required: de.sciss.collection.txn.HASkipList.Child[S(in class Leaf$mcI$sp),Int]
[error] Total time: 1 s, completed Nov 15, 2011 2:28:05 PM
are there any tricks to get more information from this -- like which is the line that crashes the compiler?
best, -sciss-
On 8 Nov 2011, at 16:33, Rex Kerr wrote:
[info] Compiling 47 Scala sources and 11 Java sources to /Users/hhrutz/Documents/devel/TreeTests/target/scala-2.9.1/classes...
[info] [running phase parser on 58 compilation units]
[info] [running phase namer on 58 compilation units]
[info] [running phase packageobjects on 58 compilation units]
[info] [running phase typer on 58 compilation units]
[info] [running phase superaccessors on 58 compilation units]
[info] [running phase pickler on 58 compilation units]
[info] [running phase refchecks on 58 compilation units]
[info] [running phase liftcode on 58 compilation units]
[info] [running phase uncurry on 58 compilation units]
[info] [running phase tailcalls on 58 compilation units]
[info] [running phase specialize on 58 compilation units]
[error] {file:/Users/hhrutz/Documents/devel/TreeTests/}default-92482a/compile:compile: scala.tools.nsc.symtab.Types$TypeError: type mismatch;
[error] found : HASkipList.this.Node[S(in class Leaf),A]
[error] required: HASkipList.this.Child[S(in class Leaf$mcI$sp),scala.this.Int]
[error] Total time: 9 s, completed Nov 15, 2011 3:03:34 PM
i will see if i can build with 2.10, but i have various dependencies, so probably not. might need to add -no-specialization and screw this wasted attempt...
@tailrec def step( num: Int, n: Node[ S, A ]) : Int = n match {
case Leaf( _ ) => num
case Branch( b ) => step( num + 1, b.down( 0 ))
}
with my extractor objects like this to avoid warnings due to type erasure:
object Node {
def unapply[ S <: Sys[ S ], @specialized( Int ) A ]( child: Child[ S, A ]) : Option[ Node[ S, A ]] = child.nodeOption
}
so this was somehow crashing the compiler/specialization. how if i go back to crappy warnings:
@tailrec def step( num: Int, n: Node[ S, A ]) : Int = n match {
case _: Leaf[ _, _ ] => num
case b: Branch[ S, A ] => step( num + 1, b.down( 0 ))
}
i get a strange warning:
warn] /Users/hhrutz/Documents/devel/TreeTests/src/main/scala/de/sciss/collection/txn/HASkipList.scala:155: non variable type-argument S in type pattern de.sciss.collection.txn.HASkipList.Branch[S,A] is unchecked since it is eliminated by erasure
[warn] case b: Branch[ S, A ] => step( num + 1, b.down( 0 ))
[warn] ^
...
[warn] missing combination Leaf
[warn] missing combination Leaf$mcI$sp
[warn] @tailrec def step( num: Int, n: Node[ S, A ]) : Int = n match {
[warn] ^
a guess this latter warning is a bug with the interaction between pattern matching and specialization?
also i'm curious why i only get a warning about the erasure of `S` (my list's transactional system), but not because of `A` (my list's element type, specialized)
best, -sciss-
class Test1[ @specialized( Int ) A ] {
def test( a: A ) {
sys.error( "here" )
}
}
val t1 = new Test1[ Int ]
t1.test(0)
// ok:
// java.lang.RuntimeException: here
// at scala.sys.package$.error(package.scala:27)
// at Test1$mcI$sp.test$mcI$sp(<console>:34)
abstract class Test2[ @specialized( Int ) A ] {
def test( a: A ) {
sys.error( "here" )
}
}
def ano[ @specialized( Int ) A ] : Test2[ A ] = new Test2[ A ] {}
val t2 = ano[ Int ]
t2.test( 1 )
// not ok:
// java.lang.RuntimeException: here
// at scala.sys.package$.error(package.scala:27)
// at Test2.test(<console>:34)
// at Test2.test$mcI$sp(<console>:33)
:-(
best, -sciss-
On 7 Nov 2011, at 23:04, Aleksey Nikiforov wrote:
trait Test4[ @specialized( Int ) A ] {
def test( a: A ) {
sys.error( "here" )
}
}
def ano2[ @specialized( Int ) A ] : Test4[ A ] = new Test4[ A ] {}
val t4 = ano2[ Int ]
t4.test(0) // OK!
class Test5[ @specialized( Int ) A ] extends Test4[ A ]
val t5 = new Test5[ Int ]
t5.test(0) // Not OK!
Specialization does compose, but in a slightly different way. The choice to use specialization is made at the CALL SITE. The limitations of specialization are the consequences of that. What is boils down to is that specialization only composes at the method level, and your methods must have at least one argument that have the type marked as specialized.object Main {def main(args: Array[String]) {val t = new Test2(new IntObs, 1)t.bar // not specializedt.foo // not specializedt.foo[Int] // still not specializedt.foo(1) // specialized}}trait KeyObserver[ @specialized( Int ) A ] { def keyUp( k: A ) : Unit }
class IntObs extends KeyObserver[Int] { def keyUp( key: Int ) { } }class AnyObs extends KeyObserver[Any] { def keyUp( key: Any ) { } }class Test2[ @specialized( Int ) A ]( obs: KeyObserver[ A ], lala: A ) {obs.keyUp( lala )def bar() {obs.keyUp( lala )}def foo[X <: A]() { // will produce a specialized method that is never used.obs.keyUp( lala )}def foo(a: A) {obs.keyUp(a)}}Constructors look like methods with specialized signatures, but they are not specialized.Of course specialization also composes by extending specialized traits. Extending specialized classes is somewhat buggy and requires intimate knowledge of what goes on under the hood. If you try extending a specialized class you will get a compiler warning.Overall, using specialization is a bit like walking on the minefield. One wrong step - and primitives get boxed. So I suggest you get a decent decompiler and verify your code. Here is the one I use: http://java.decompiler.free.fr/
[...]
On 16 Nov 2011, at 22:27, iulian dragos wrote:
>
> You could also use this compiler plugin:
>
> https://github.com/dragos/noboxing-plugin
>
> You'd need to build it yourself and it was written for 2.8, but it should work out of the box with 2.9.
>
> iulian
[...]