- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Type specialization
Mon, 2009-11-23, 04:50
Hello,
Maybe it was too early to fire bugs and I should discuss this on the mailing
list first.
https://lampsvn.epfl.ch/trac/scala/ticket/2678
https://lampsvn.epfl.ch/trac/scala/ticket/2679
What I was trying to do is implement specialized array search,
I have array with objects and array with their integer hashcodes.
When I look for an object I want first check its referential equality,
if it equals - OK, if not - first compare hashcode and only
if it equals - call Object.equals method to compare them.
There is eq operator but as far as I can understand it works only on
AnyRef/Object types.
One proposal it to make it work on any scala type just as == works in java.
Now I can write something like this:
class Test[@specialized A] (implicit val m: Manifest[A]) {
def test (x: A, y: A) = if (x.isInstanceOf[AnyRef]) x.asInstanceOf[AnyRef]
eq y.asInstanceOf[AnyRef] else x == y
}
But when I decompile resulting code specialized for Int I see:
public boolean test$mcI$sp(int x, int y)
{
return (BoxesRunTime.boxToInteger(x) instanceof Object) ?
BoxesRunTime.boxToInteger(x) == BoxesRunTime.boxToInteger(y) :
BoxesRunTime.equals(BoxesRunTime.boxToInteger(x),
BoxesRunTime.boxToInteger(y));
}
This code is just incorrect since Int is not an instance of AnyRef,
but BoxesRunTime.boxToInteger(x) - is!
Is it possible to make some king of conditional compilation for specialized
types?
Not like #ifdef, but at least so that compiler could check types and remove
some code,
so there would be no ".isInstanceOf[AnyRef]" check for Int specialization
since
compiler already know its type. It would be excellent if this will work in
match/case too, like:
class Test[@specialized A] (implicit val m: Manifest[A]) {
def test (x: A, y: A) = x match {
case r: AnyRef => r eq y.asInstanceOf[AnyRef]
case i: Int => i == y
case _ => x == y
}
}
So that when specialized to Int only "i == y" check will be done since we
know the types.
Currently this code produces just something ugly. Even simple == when
specialized
generates too much boxing and boxed checks that there's almose no point in
specialization at all.
class Test[@specialized A] (implicit val m: Manifest[A]) {
def test (x: A, y: A) = x == y
}
When specialzied to Int produces the following decompiled code:
public boolean test$mcI$sp(int x, int y)
{
return BoxesRunTime.equals(BoxesRunTime.boxToInteger(x),
BoxesRunTime.boxToInteger(y));
}
just to compare two ints! This is very inefficient.
Also it would be nice to simplify specialized arrays creation. Now it
requires manifests,
so calling new Array[A](n) always calls manifest.newArray(n) but in
specialized code
we know exact types and it is better just to have analog of "new int[n]"
java code.
Regards,
Alex
-----
Faster HashMap: http://github.com/alex14n/CompactHashMap
Mon, 2009-11-23, 12:47
#2
Re: Type specialization
On Mon, Nov 23, 2009 at 2:10 PM, Iulian Dragos wrote:
> On Mon, Nov 23, 2009 at 4:50 AM, Alex Yakovlev wrote:
>> There is eq operator but as far as I can understand it works only on
>> AnyRef/Object types.
>
> That is true, it is for reference equality.
Is it possible to introduce a complete equivalent of java's == into Scala?
I mean, that will work on any types (including primitive ones), not just
AnyRef?
> This is an instance of this bug report
>
> https://lampsvn.epfl.ch/trac/scala/ticket/1872
>
> which says that instanceOf checks on primitive types will be disallowed.
> I'm
> not sure what that means for polymorphic types, which could be primitives.
What I'm afraid is that it will just cause errors, like for example:
class Test[@specialized A] (implicit val m: Manifest[A]) {
def test (x: A, y: A) = x match {
case i: Int => i == y
case r: AnyRef => r eq y.asInstanceOf[AnyRef]
case _ => x == y
}
}
does not compile with -Yspecialize at all, but compiles without this switch.
Is it a bug or what? Compiles says:
Test.scala:4: error: unreachable code
case r: AnyRef => r eq y.asInstanceOf[AnyRef]
^
one error found
I'm afraid it was caused by one of specialized branches,
for Int, where first "case" will always match thus making other
"cases" really unreachable. That's exactly the case where
I suppose conditional compilation is required, but that's just
a guess since I do not know the cause of this error.
I suppose we can detect such cases where we can just remove
some of the branches in if and match on specialized types.
>> Is it possible to make some king of conditional compilation for
>> specialized types? Not like #ifdef, but at least so that compiler
>> could check types and remove some code, so there would be no
>> ".isInstanceOf[AnyRef]" check for Int specialization since
>> compiler already know its type. It would be excellent if this
>> will work in match/case too, like:
>>
>> class Test[@specialized A] (implicit val m: Manifest[A]) {
>> def test (x: A, y: A) = x match {
>> case r: AnyRef => r eq y.asInstanceOf[AnyRef]
>> case i: Int => i == y
>> case _ => x == y
>> }
>> }
>
> This makes sense, but I'm not yet sure how far this could go. The reason
> is
> that specialized code may return different results from non-specialized
> code. Unless the dynamic instance check on primitives can be fixed.
Well, actually that's what I'd like to see - a possibility to intentionally
make possible to have different pieces of code for different specialized
types,
in this case user will take complete responsibility for all possible harm.
Maybe there's a sense to emit a warning, and some annotation to disable it
in case you do that on purpose.
>> Also it would be nice to simplify specialized arrays creation. Now it
>> requires manifests, so calling new Array[A](n) always calls
>> manifest.newArray(n) but in specialized code we know exact types
>> and it is better just to have analog of "new int[n]" java code.
>
> Agreed. But since there's just one definition of a specialized class, how
> to
> handle the generic case? Maybe have user-defined specialized classes, or a
> 'specialized' version for Object, which is really the generic one.
In generic case we have only 2 possibilities, either to use a manifest,
or allocate generic array of Objects and then store boxed values in it,
but since manifests were already introduces I think it should be used.
And in specialized code we can simply ignore it.
Regards,
Alex
-----
Faster HashMap: http://github.com/alex14n/CompactHashMap
On Mon, Nov 23, 2009 at 4:50 AM, Alex Yakovlev <alex14n@gmail.com> wrote:
That is true, it is for reference equality.
This is an instance of this bug report
https://lampsvn.epfl.ch/trac/scala/ticket/1872
which says that instanceOf checks on primitive types will be disallowed. I'm not sure what that means for polymorphic types, which could be primitives.
This makes sense, but I'm not yet sure how far this could go. The reason is that specialized code may return different results from non-specialized code. Unless the dynamic instance check on primitives can be fixed.
This is plainly a bug. I am not sure what happens, but it's not intended. At all.
Agreed. But since there's just one definition of a specialized class, how to handle the generic case? Maybe have user-defined specialized classes, or a 'specialized' version for Object, which is really the generic one.
cheers,
iulian
--
« Je déteste la montagne, ça cache le paysage »
Alphonse Allais