Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use a view bound in a Scala value class?

Tags:

generics

scala

While dealing with some Java code, I wanted to find a way to reduce a Raw Set to include its parameterized type.

I also wanted it to work for Scala sets as well, so I did the following

implicit class Harden[S <% mutable.Set[_]](val set: S) extends AnyVal {
    def cast[T] = set.map(_.asInstanceOf[T])
}

That resulted in a compiler error that I didn't expect

Error:(27, 27) field definition is not allowed in value class
  implicit class Harden[S <% mutable.Set[_]](val set: S) extends AnyVal {

I didn't find any mention of this type of restriction in the Scala View Bounds or Value Class documentation.

Why is this not allowed? I'm using Scala 2.10.3.

like image 400
kanielc Avatar asked Jan 10 '23 16:01

kanielc


1 Answers

As you can see from this sbt console output:

scala> :type implicit class Harden[S <% mutable.Set[_]](val set: S)
[S]AnyRef {
  val set: S
  private[this] val set: S
  implicit private[this] val evidence$1: S => scala.collection.mutable.Set[_]
  def <init>(set: S)(implicit evidence$1: S => scala.collection.mutable.Set[_]): Harden[S]
}

... the actual constructor of Harden desugars behind the scenes to:

def <init>(set: S)(implicit evidence$1: S => scala.collection.mutable.Set[_]): Harden[S]

... (i.e. takes set in one argument list and implicit evidence$1 in another).

As described in value classes limitations here:

must have only a primary constructor with exactly one public, val parameter whose type is not a value class.

... whitch means that Harden violaties this limitation.

You can achieve something similar, howerver. Try to convert your view bound defined on class to implicit evidence on method instead.

Something like this:

scala> implicit class Harden[S](val set: S) extends AnyVal {
     |     def cast[T](implicit ev: S => scala.collection.mutable.Set[_]) = set.map(_.asInstanceOf[T])
     | }
defined class Harden

This will compile:

scala> Set(1,2,3).cast[Any]
res17: scala.collection.mutable.Set[Any] = Set(1, 2, 3)

And this will fail, as expected:

scala> List(1,2,3).cast[Any]
<console>:24: error: No implicit view available from List[Int] => scala.collection.mutable.Set[_].
              List(1,2,3).cast[Any]
                              ^
like image 103
Eugene Loy Avatar answered Jan 18 '23 16:01

Eugene Loy