Workaround for value class type erasure?

154 views
Skip to first unread message

Mark Hammons

unread,
May 9, 2013, 8:52:34 AM5/9/13
to scala-l...@googlegroups.com
So it appears to me that value classes have their types erased to the type they take for their single value. This of course causes the standard type erasure issues to pop up. 

Right now I'm having trouble with overloading. I have function a with signatures Int => UInt and UInt => UInt. It fails to compile because of double definition. Is there a way to get past this issue?

--
Mark Edgar Hammons II - Research Engineer at the ISCPIF | Contributor to OpenMOLE

Cell - +33 06 03 69 56 56 | + 1 405 928 8206

Erik Osheim

unread,
May 9, 2013, 9:17:42 AM5/9/13
to scala-l...@googlegroups.com
On Thu, May 09, 2013 at 02:52:34PM +0200, Mark Hammons wrote:
> Right now I'm having trouble with overloading. I have function a with
> signatures Int => UInt and UInt => UInt. It fails to compile because of
> double definition. Is there a way to get past this issue?

I don't think you can do this without introducing a third type:

case class UIntBoxed(u: UInt) { ... }

class UInt(val n: Int) extends AnyVal {
implicit def box = UIntBoxed(this)
}

object Qux {
def apply(n: Int) = ...
def apply(n: UIntBoxed) = ...
}

Of course, this will negate the runtime benefits of using a value
class in the first place. In my opinion, overloading based solely on
value class types is antithetical to their purpose--it would be better
to use different method names or have the user provide some other
indicator of desired behavior.

-- Erik

P.S. If you're defining UInt you may be interested to compare your
implementation with Spire's:

https://github.com/non/spire/blob/2.10.0/core/src/main/scala/spire/math/UInt.scala

Sébastien Doeraene

unread,
May 9, 2013, 9:32:28 AM5/9/13
to scala-l...@googlegroups.com
Hello,

I would explore other designs first, but if you really need to have the interface you describe, you can go for default parameters:

def foo(x: Int): UInt = ???
def foo(x: UInt, dummy: Int = 0): UInt = ???

Cheers,
Sébastien



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



Mark Hammons

unread,
May 9, 2013, 10:12:41 AM5/9/13
to scala-l...@googlegroups.com
Well in this case I'm trying to make near costless unsigned types for the standard int types, so having identical interfaces would be preferred.

Nate Nystrom

unread,
May 9, 2013, 10:16:34 AM5/9/13
to scala-l...@googlegroups.com
You can also use a dummy implicit:

def foo(x: Int)(implicit d: DummyImplicit): UInt = ???
def foo(x: UInt): UInt = ???

I use this in my own UInt implementation:

https://github.com/nystrom/scala-unsigned/blob/master/src/main/scala/passera/unsigned/ULong.scala

Nate



On Thu, May 9, 2013 at 3:32 PM, Sébastien Doeraene
<sjrdo...@gmail.com> wrote:

Derek Williams

unread,
May 9, 2013, 10:26:07 AM5/9/13
to scala-l...@googlegroups.com
Type classes are a solution for this kind of thing, although it does add a bit of boilerplate.

trait Fooable[A] {
  def foo(a: A): UInt
}

object Fooable {
  def apply[A](implicit fooable: Fooable[A]) = fooable
  implicit object IntFooable extends Fooable[Int] { def foo(a: Int) = ??? }
  implicit object UIntFooable extends Fooable[UInt] { def foo(a: UInt) = ??? }
}

// No more overrides
def foo[A: Fooable](value: A): UInt = Fooable[A].foo(value)


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



--
Derek Williams

Mark Hammons

unread,
May 9, 2013, 10:33:58 AM5/9/13
to scala-l...@googlegroups.com
Would type classes add any overhead to the value class?

Jeff Olson

unread,
May 9, 2013, 10:59:29 AM5/9/13
to scala-l...@googlegroups.com
Yes, using type classes as above means the value class would get boxed. In my experience the dummy implicit idea works the best, with the least amount of overhead (especially if the implicit can be found in the companion of the value class in question).

In practice, I've been using an idea posted here: http://stackoverflow.com/questions/3422336

sealed trait __[+_]
object __ {
 implicit object instance extends __[Any]
}

def foo(x: Int): UInt = ???
def foo[_: __](x: UInt): UInt = ???

It looks like black magic, but it works well, with little overhead.

-Jeff

Erik Osheim

unread,
May 9, 2013, 11:21:16 AM5/9/13
to scala-l...@googlegroups.com
On Thu, May 09, 2013 at 07:59:29AM -0700, Jeff Olson wrote:
> It looks like black magic, but it works well, with little overhead.

That is... kind of awesome. Weird and insane, but also awesome.

For what it's worth it is possible to use type classes without
introducing any significant overhead (Spire does this and we have
benchmarks that compare direct and generic code). However it
definitely imposes a lot of extra work on the library author to try to
get this stuff right.

-- Erik

Paul Phillips

unread,
May 9, 2013, 11:24:19 AM5/9/13
to scala-l...@googlegroups.com

On Thu, May 9, 2013 at 7:59 AM, Jeff Olson <jeff.d...@gmail.com> wrote:
Yes, using type classes as above means the value class would get boxed.

You can specialize the type class. Maybe the fact that neither you nor Erik mentioned this means there's something wrong with it...

class UInt(val n: Int) extends AnyVal

trait Fooable[@specialized(Int) A] {
  def foo(a: A): UInt
}
object Fooable {
  def apply[@specialized(Int) A](implicit fooable: Fooable[A]) = fooable
  implicit object IntFooable extends Fooable[Int] { def foo(a: Int) = ??? }
  implicit object UIntFooable extends Fooable[UInt] { def foo(a: UInt) = ??? }
}
object Test {
  def foo[@specialized(Int) A: Fooable](value: A): UInt = Fooable[A].foo(value)

  def main(args: Array[String]): Unit = {
    foo(5)
    foo(new UInt(5))
  }
}

Chris Hodapp

unread,
May 9, 2013, 2:38:51 PM5/9/13
to scala-l...@googlegroups.com
Isn't it a problem that you get:
  public void main(java.lang.String[]);
    flags: ACC_PUBLIC
    Code:
      stack=4, locals=2, args_size=2
         0: aload_0
         1: iconst_5
         2: getstatic     #40                 // Field Fooable$IntFooable$.MODULE$:LFooable$IntFooable$;
         5: invokevirtual #44                 // Method foo$mIc$sp:(ILFooable;)I
         8: pop
         9: aload_0
        13: dup
        15: invokespecial #49                 // Method UInt."<init>":(I)V
        18: getstatic     #54                 // Field Fooable$UIntFooable$.MODULE$:LFooable$UIntFooable$;
        21: invokevirtual #56                 // Method foo:(Ljava/lang/Object;LFooable;)I
        24: pop
        25: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      26     0  this   LTest$;
               0      26     1  args   [Ljava/lang/String;
?

Aren't we running afoul of our goal of not boxing?

Jason Zaugg

unread,
May 9, 2013, 4:11:19 PM5/9/13
to scala-l...@googlegroups.com
On Thu, May 9, 2013 at 8:38 PM, Chris Hodapp <clho...@gmail.com> wrote:
Isn't it a problem that you get:
        21: invokevirtual #56                 // Method foo:(Ljava/lang/Object;LFooable;)I

Aren't we running afoul of our goal of not boxing?

It's no easy thing to fix, but here's the ticket:

"Use of a Value Class as a type argument for a specialized parameter could avoid boxing."


-jason

Shelby

unread,
Jun 19, 2015, 10:03:48 PM6/19/15
to scala-l...@googlegroups.com, nate.n...@usi.ch
Could we get native UInt and ULong in Dotty please? That has really annoyed (not only) me about Java and Scala.

Why can't the compiler handle the necessary boilerplate required for each VM?

For now the compiler could just stub in your library.
Reply all
Reply to author
Forward
0 new messages