Type erasure bites again :-(

63 views
Skip to first unread message

Paul Butcher

unread,
Jun 27, 2011, 7:53:10 PM6/27/11
to scala-user
I could get to hate type erasure with a fiery passion :-(

This compiles fine:

> class A {
> def m(x: Int) = "x is: "+ x
> def m(x: Double) = "x is: "+ x
> }

But this fails because the overload disappears in the presence of type erasure:

> class B {
> def m(x: Option[Int]) = "x is: "+ x
> def m(x: Option[Double]) = "x is: "+ x
> }

Any suggestions on good ways to work around this?

Why do I need to do this? I'm working on making Borachio (Scala mocking library) type safe. I'm pretty much there, except when dealing with overloaded methods. The problem is that mock methods, instead of taking the "raw" parameter types, take a MockParameter. So:

> def foo(x: Double)

becomes (in the mock object):

> def foo(x: MockParameter[Double])

Which means that as well as specifying specific values, e.g:

> m.expects.foo(1.23)


Wildcards and epsilon matches can also be used, e.g:

> m.expects.foo(*)
> m.expects.foo(~1.23)

This is all working just fine for non-overloaded methods. But for overloaded methods, I hit the type erasure problem above :-(

Suggestions gratefully received!

--
paul.butcher->msgCount++

Snetterton, Castle Combe, Cadwell Park...
Who says I have a one track mind?

http://www.paulbutcher.com/
LinkedIn: http://www.linkedin.com/in/paulbutcher
MSN: pa...@paulbutcher.com
AIM: paulrabutcher
Skype: paulrabutcher

Bill Venners

unread,
Jun 27, 2011, 8:19:30 PM6/27/11
to Paul Butcher, scala-user
Hi Paul,

If you're generating code, you can generate subtypes of those types,
though I'm not sure that would solve your particular problem. But for
example:

class DoubleMockParameter extends MockParameter[Double]
class IntMockParameter extends MockParameter[Int]

You can overload those two subtypes just fine.

Bill

--
Bill Venners
Artima, Inc.
http://www.artima.com

Seth Tisue

unread,
Jun 27, 2011, 8:30:41 PM6/27/11
to scala-user
On Mon, Jun 27, 2011 at 7:53 PM, Paul Butcher <pa...@paulbutcher.com> wrote:
> But this fails because the overload disappears in the presence of type erasure: [...]

> I could get to hate type erasure with a fiery passion :-(

Don't use all your hatred up on type erasure, save some of it for
overloading...!
A lot of it, even.

--
Seth Tisue | Northwestern University | http://tisue.net
lead developer, NetLogo: http://ccl.northwestern.edu/netlogo/

Debasish Ghosh

unread,
Jun 28, 2011, 2:22:19 AM6/28/11
to Paul Butcher, scala-user
One approach to resolve such overloads is to use typeclasses ..

import B._
class B {
  def m[A: BP](x: Option[A]) = implicitly[BP[A]] m x
}
object B {
  sealed trait BP[A] {
    def m(x: Option[A])
  }

  implicit object BPInt extends BP[Int] {
    def m(x: Option[Int]) = println("x is: " + x)
  }
  implicit object BPDouble extends BP[Double] {
    def m(x: Option[Double]) = println("x is: " + x)
  }
}

val b = new B
b.m(Some(10))
b.m(Some(10.toDouble))

HTH.

Paul Butcher

unread,
Jun 28, 2011, 4:01:30 AM6/28/11
to Seth Tisue, scala-user
On 28 Jun 2011, at 01:30, Seth Tisue wrote:
> Don't use all your hatred up on type erasure, save some of it for
> overloading...!
> A lot of it, even.

Unfortunately, given that the language supports the feature, I have little choice but to also do so, irrespective of my feelings about it.

(although, TBH, it's not something that I feel strongly about either way).

Paul Butcher

unread,
Jun 28, 2011, 4:03:29 AM6/28/11
to Bill Venners, scala-user
Thanks, Bill - I've managed to get something good enough for the time being working with this approach.

Paul Butcher

unread,
Jun 28, 2011, 4:05:44 AM6/28/11
to dgh...@acm.org, scala-user
Thanks Debasish,

I'm going to have to stare at that for a while to understand how it works, but it looks good :-)

Kevin Wright

unread,
Jun 28, 2011, 5:09:22 AM6/28/11
to Paul Butcher, dgh...@acm.org, scala-user
On 28 June 2011 09:05, Paul Butcher <pa...@paulbutcher.com> wrote:
Thanks Debasish,

I'm going to have to stare at that for a while to understand how it works, but it looks good :-)

On 28 Jun 2011, at 07:22, Debasish Ghosh wrote:
One approach to resolve such overloads is to use typeclasses ..

import B._
class B {
  def m[A: BP](x: Option[A]) = implicitly[BP[A]] m x
}
object B {
  sealed trait BP[A] {
    def m(x: Option[A])
  }

  implicit object BPInt extends BP[Int] {
    def m(x: Option[Int]) = println("x is: " + x)
  }
  implicit object BPDouble extends BP[Double] {
    def m(x: Option[Double]) = println("x is: " + x)
  }
}

val b = new B
b.m(Some(10))
b.m(Some(10.toDouble))

HTH.

Okay, this should help get you going...


This code creates a "type class" BP, consisting of:
- The abstract trait `BP` (abstract because `m` is undefined)
- The implicit instance `BPInt`, parameterised with the type `Int`
- The implicit instance `BPDouble` paramaterised with the type `Double`
 
This is then used in the `B.m` method

def m[A: BP](x: Option[A]) = ...
The [A : BP] specifies a "context bound", which de-sugars to:
def m[A](x: Option[A])(implicit ev$1: BP[A]) = ...
Requiring that for the supplied type `A`, there must exist an implicit instance of `BP[A]`.  The name `ev$1` is synthetically generated and you can't rely on it to be stable, but the value *is* pulled into the implicit scope within the method, which is then defined as:

def m[A: BP](x: Option[A]) = implicitly[BP[A]] m x
With `implicitly` being a method defined in predef that looks up some value of a given type from the implicit scope.  This will invoke the method `m` from the discovered instance of `BP`, supplying `x` as an argument.  There are several variations on this theme; for example, the `B.m` method could have been written:

def m[A](x: Option[A])(implicit bp: BP[A]) = bp.m(x)

Another common pattern is the `XisY` naming convention, as used by scala.math.Numeric and subclasses. Where instead of `BPInt` and `BPDouble` you'd names such as `IntIsBP` and `DoubleIsBP`.

You also don't *have* to create singletons, an instance of a type class can be defined as anything that you're able to make implicit, so using vals with anonymous classes (for instance) is also perfectly valid.

The real beauty of this approach is that you can create a single generic instance of `B.m`, not using overloading, but (unlike alternatives using reflection) it will still be statically safe and will yield a compile-time error if called with an argument who's type doesn't have an instance of `BP` provided.



--
Kevin Wright

gtalk / msn : kev.lee...@gmail.com
mail: kevin....@scalatechnology.com
vibe / skype: kev.lee.wright
quora: http://www.quora.com/Kevin-Wright
twitter: @thecoda

"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra

Paul Butcher

unread,
Jun 28, 2011, 7:38:33 AM6/28/11
to Kevin Wright, dgh...@acm.org, scala-user
On 28 Jun 2011, at 10:09, Kevin Wright wrote:
> Okay, this should help get you going...

Thanks, Kevin - that's very helpful indeed.

Possibly a silly question, but is there any significance to the choice of names (i.e. "B" and "BP")?

Debasish Ghosh

unread,
Jun 28, 2011, 7:41:01 AM6/28/11
to Paul Butcher, Kevin Wright, scala-user
Actually I intended to make it BParam (parameter for B) .. but laziness took over :-) and now that u mention it I think it should have been MP (parameter for method m) .. but anyway ..

Norbert Tausch

unread,
Jun 28, 2011, 8:11:49 AM6/28/11
to scala...@googlegroups.com
Is type erasure only a point to the JVM? Has .Net the same mechanism or problem?

It seems that - although there are workarounds - type erasure makes Scala code more complicated.

Is there any chance that Scala can overcome this 'limitation' in future?
Best regards

Norbert Tausch

Kevin Wright

unread,
Jun 28, 2011, 8:29:08 AM6/28/11
to Norbert Tausch, scala...@googlegroups.com
On 28 June 2011 13:11, Norbert Tausch <ntau...@gmail.com> wrote:
Is type erasure only a point to the JVM? Has .Net the same mechanism or problem?

It's certainly not unique to the JVM, most languages using a virtual machine take the same approach.  Nor should it be automatically be seen as a problem, erasure has some definite benefits in terms of performance and simplifying the VM implementation.

.NET *does* reify types, in order to achieve this the platform had to implement an entirely new collections framework in parallel with with the existing one, and sacrificed backwards compatibility in doing so, this would have been a much harder idea to sell if .NET adoption hadn't been so small at the time.

Contrast this to Java, which was already widely adopted at the time of adding generics.  It's also worth noting that a reified JVM would have left us stuck with use-site variance, and severely limited Scala's ability to favour declaration-site variance.
 

It seems that - although there are workarounds - type erasure makes Scala code more complicated.

In some scenarios, yes.  Although in practice this usually only happens when combined with other language features - such as overloading or reflection.

Arguably, (and *many* on this list would agree here) there's far more justification in pointing any finger of blame at overloading and reflection...
 
Is there any chance that Scala can overcome this 'limitation' in future?

Of course, this is exactly the sort of thing that we use type classes and manifests for!  It's also reasonable to expect that manifests will be more tightly integrated into other language features (such as pattern matching) with future versions of the language, making the downsides of erasure less and less apparent as time goes on (though not sacrificing the upsides)


 
Best regards

Norbert Tausch

Am 28.06.2011 13:41, schrieb Debasish Ghosh:
Actually I intended to make it BParam (parameter for B) .. but laziness took over :-) and now that u mention it I think it should have been MP (parameter for method m) .. but anyway ..


On Tue, Jun 28, 2011 at 5:08 PM, Paul Butcher <pa...@paulbutcher.com> wrote:
On 28 Jun 2011, at 10:09, Kevin Wright wrote:
> Okay, this should help get you going...

Thanks, Kevin - that's very helpful indeed.

Possibly a silly question, but is there any significance to the choice of names (i.e. "B" and "BP")?

--
paul.butcher->msgCount++

Snetterton, Castle Combe, Cadwell Park...
Who says I have a one track mind?

http://www.paulbutcher.com/
LinkedIn: http://www.linkedin.com/in/paulbutcher
MSN: pa...@paulbutcher.com
AIM: paulrabutcher
Skype: paulrabutcher




--
Debasish Ghosh
http://manning.com/ghosh

Twttr: @debasishg
Blog: http://debasishg.blogspot.com
Code: http://github.com/debasishg

Matthew Pocock

unread,
Jun 28, 2011, 8:29:43 AM6/28/11
to Norbert Tausch, scala...@googlegroups.com
Hi,

On 28 June 2011 13:11, Norbert Tausch <ntau...@gmail.com> wrote:
Is type erasure only a point to the JVM? Has .Net the same mechanism or problem?

In the JVM, the parameter types are erased. This is an architectural decision of the JVM. In C++, and on .Net, the fully parameterised type is present at run-time. So in Java you have a single class java.util.List for List<Integer>, List<String>, List<Address> and so on. In .Net and C++, you have an independent type (and possibly completely independent code) for each of these.
 

It seems that - although there are workarounds - type erasure makes Scala code more complicated.

Type erasure gives with one hand and takes with the other. Code size remains small, allowing the optimizers to do work once and total code footprints to be smaller. Dynamic dispatch based upon the parameterizing type is not possible. However, there are workarounds (e.g. using manifests for case-based lookup or using typeclasses for dynamic dispatch).
 

Is there any chance that Scala can overcome this 'limitation' in future?

In my personal view, it's more a case of identifying where erasure bites scala users consistently and providing documentation, code patterns, libraries and possibly compiler support to alleviate these.

Matthew
 
Best regards

Norbert Tausch

Am 28.06.2011 13:41, schrieb Debasish Ghosh:
Actually I intended to make it BParam (parameter for B) .. but laziness took over :-) and now that u mention it I think it should have been MP (parameter for method m) .. but anyway ..


On Tue, Jun 28, 2011 at 5:08 PM, Paul Butcher <pa...@paulbutcher.com> wrote:
On 28 Jun 2011, at 10:09, Kevin Wright wrote:
> Okay, this should help get you going...

Thanks, Kevin - that's very helpful indeed.

Possibly a silly question, but is there any significance to the choice of names (i.e. "B" and "BP")?

--
paul.butcher->msgCount++

Snetterton, Castle Combe, Cadwell Park...
Who says I have a one track mind?

http://www.paulbutcher.com/
LinkedIn: http://www.linkedin.com/in/paulbutcher
MSN: pa...@paulbutcher.com
AIM: paulrabutcher
Skype: paulrabutcher




--
Debasish Ghosh
http://manning.com/ghosh

Twttr: @debasishg
Blog: http://debasishg.blogspot.com
Code: http://github.com/debasishg



--
Dr Matthew Pocock
Visitor, School of Computing Science, Newcastle University
(0191) 2566550

Norbert Tausch

unread,
Jun 28, 2011, 8:37:29 AM6/28/11
to Kevin Wright, Norbert Tausch, scala...@googlegroups.com
Thanks for the explanation!

Pattern matching and overloading are indeed the most relevant points to me.
Best regards

Norbert Tausch

Lars Hupel

unread,
Jun 28, 2011, 11:46:08 AM6/28/11
to scala...@googlegroups.com
I'd like to throw in the concept of 'super type tokens', as described in
this posting by Neal Gafter:

<http://gafter.blogspot.com/2006/12/super-type-tokens.html>

Although I haven't employed it in Scala yet (no need at the moment), I
think it could help in certain circumstances.

Ken Bloom

unread,
Jun 28, 2011, 2:39:46 PM6/28/11
to scala...@googlegroups.com
On Tue, 28 Jun 2011 00:53:10 +0100, Paul Butcher wrote:

> I could get to hate type erasure with a fiery passion :-(
>
> This compiles fine:
>
>> class A {
>> def m(x: Int) = "x is: "+ x
>> def m(x: Double) = "x is: "+ x
>> }
>
> But this fails because the overload disappears in the presence of type
> erasure:
>
>> class B {
>> def m(x: Option[Int]) = "x is: "+ x
>> def m(x: Option[Double]) = "x is: "+ x
>> }

Like this:

class B{
def m(x: Option[Int]) = "x is: "+x

def m(x: Option[Int])(implicit sentinel:DummyImplicit) = "x is: "+x
}

If you have other overloads, just make sure they have different numbers
of DummyImplicit parameters. DummyImplicit is defined in Predef, so it's
always available.

--Ken

--
Chanoch (Ken) Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/

Raoul Duke

unread,
Jun 28, 2011, 4:23:09 PM6/28/11
to scala-user
On Tue, Jun 28, 2011 at 11:39 AM, Ken Bloom <kbl...@gmail.com> wrote:
> If you have other overloads, just make sure they have different numbers
> of DummyImplicit parameters. DummyImplicit is defined in Predef, so it's
> always available.

wow. i don't know jack about making or fixing languages, but i sure
hope somebody can come up with a better alternative some day :-}

Ken Bloom

unread,
Jul 4, 2011, 9:00:16 PM7/4/11
to scala...@googlegroups.com

On the other hand, if Sun had designed reified generics in the JVM,
we'd probably stuck with a version of reified generics that didn't
allow covariance and contravariance. So count your lucky stars that
the type erasure problem here can actually be resolved pretty easily
with a relatively unintrusive hack.

Fabio Cechinel Veronez

unread,
Jul 5, 2011, 7:43:39 AM7/5/11
to Ken Bloom, scala...@googlegroups.com
humm.. isn't it possible to implement covariance and contravariance
with reified types?
how scala for .net deals with it?

--
Fabio Cechinel Veronez

Kevin Wright

unread,
Jul 5, 2011, 7:50:07 AM7/5/11
to Fabio Cechinel Veronez, Ken Bloom, scala...@googlegroups.com
It's possible, but Java implemented a use-site variance scheme whereas .NET and Scala have a declaration-site scheme.

If Java had reified use-site variance, then things would be a great deal harder for Scala.

--
Kevin Wright

gtalk / msn : kev.lee...@gmail.com

Debasish Ghosh

unread,
Jul 5, 2011, 7:57:52 AM7/5/11
to Kevin Wright, Fabio Cechinel Veronez, Ken Bloom, scala...@googlegroups.com
Here's a blog post from Ola Bini where he argues that the current generics implementation is well suited for a multi language VM like the Java virtual machine .. http://olabini.com/blog/2010/07/questioning-the-reality-of-generics/ .. here is one very relevant snippet from the post ..

What this all means is that if you want to add reified generics to the JVM, you should be very certain that that implementation can encompass both all static languages that want to do innovation in their own version of generics, and all dynamic languages that want to create a good implementation and a nice interfacing facility with Java libraries. Because if you add reified generics that doesn’t fulfill these criteria, you will stifle innovation and make it that much harder to use the JVM as a multi language VM.
Reply all
Reply to author
Forward
0 new messages