I have a program structured as:
abstract class IntQueue {
def get(): Int
def put(x: Int)
}
trait Doubling extends IntQueue{
abstract override def put(x: Int) {
println("In Doubling's put")
super.put(2*x)
}
}
trait Incrementing extends IntQueue {
abstract override def put(x: Int) {
println("In Incrementing's put")
super.put(x + 1)
}
}
class BasicIntQueue extends IntQueue {
private val buf = new ArrayBuffer[Int]
def get() = buf.remove(0)
def put(x: Int) {
println("In BasicIntQueue's put")
buf += x
}
}
When I do:
val incrThendoublingQueue = new BasicIntQueue with Doubling with
Incrementing
incrThendoublingQueue.put(10)
println(incrThendoublingQueue.get())
Output is:
In Incrementing's put
In Doubling's put
In BasicIntQueue's put
22
I am a bit confused about ordering here. My understanding of linearization order for this scenario is:
BasicIntQueue -> Incrementing -> Doubling -> IntQueue -> AnyRef -> Any
So when I call put, shouldn't BasicIntQueue's version be called first?
No. The linearization in this case is
{<anonymous-local>, Incrementing, Doubling, BasicIntQueue, IntQueue, AnyRef, Any}
The BasicIntQueue serves a the base of the mixin stack, with Doubling wrapping around it, and then Incrementing wrapping around both Basic and Doubling. So, when you call put on the entire thing, the outermost Incrementing is hit first, then the Doubling, then finally the Basic. The Basic must come last, because ultimately, it cannot delegate the actual insertion of the elements into the buffer to anyone else.
You can:
Just read the spec and convince yourself that it must be the case
Implement a toy-version of the algorithm from the spec, see what it outputs for various class definitions (it's somewhat enlightening, but mostly done for fun)
Reading the spec
The section 5.1.2 of the Spec
tells you precisely how the linearization is computed. You seem to have forgotten to reverse the indices 1 ... n in L(c_n) + ... + L(c_1).
If you apply the correct formula, you get the following linearizations for the involved traits and base classes:
IntQueue : {IntQueue, AnyRef, Any}
Doubling : {Doubling, IntQueue, AnyRef, Any}
Incrementing : {Incrementing, IntQueue, AnyRef, Any}
BasicIntQueue : {BasicIntQueue, IntQueue, AnyRef, Any}
If you then finally combine these linearizations to compute the linearization of the anonymous local class that is instantiated as incrThendoublingQueue:
<anonymous-local-class>, L(Incrementing) + L(Doubling) + L(BasicInt)
you obtain the linearization already shown above. Therefore, the methods should be invoked in this order:
incrementing
doubling
basic
which is consistent with the actual output.
Reimplementing linearization algorithm for fun
This is actually one of those dependency-free snippets of the specification that you can implement easily from scratch. The
definition of concatenation with replacement can be copied from
the spec as-is, it's almost runnable code (except that the funny plus with arrow is somewhat difficult to type, and that I wanted it as an infix operator on lists):
implicit class ConcatenationWithReplacementOps[A](list: List[A]) {
def +^->(other: List[A]): List[A] = list match {
case Nil => other
case h :: t =>
if (other contains h) (t +^-> other)
else h :: (t +^-> other)
}
}
Modeling a class declaration C extends C1 with ... with Cn is also
really simple:
case class ClassDecl(c: String, extendsTemplate: List[ClassDecl]) {
def linearization: List[String] = c :: (
extendsTemplate
.reverse
.map(_.linearization)
.foldLeft(List.empty[String])(_ +^-> _)
)
}
The formula for linearization is implemented as a method here. Notice the reverse.
The example given in the spec:
val any = ClassDecl("Any", Nil)
val anyRef = ClassDecl("AnyRef", List(any))
val absIterator = ClassDecl("AbsIterator", List(anyRef))
val richIterator = ClassDecl("RichIterator", List(absIterator))
val stringIterator = ClassDecl("StringIterator", List(absIterator))
val iter = ClassDecl("Iter", List(stringIterator, richIterator))
println(iter.linearization.mkString("{", ", ", "}"))
produces the output exactly as in the spec:
{Iter, RichIterator, StringIterator, AbsIterator, AnyRef, Any}
Now, here is a model of your example:
val intQueue = ClassDecl("IntQueue", List(anyRef))
val doubling = ClassDecl("Doubling", List(intQueue))
val incrementing = ClassDecl("Incrementing", List(intQueue))
val basicQueue = ClassDecl("BasicIntQueue", List(intQueue))
val incrThendoublingQueue = ClassDecl(
"<anonymous-local>",
List(basicQueue, doubling, incrementing)
)
println(incrThendoublingQueue.linearization.mkString("{", ", ", "}"))
It produces the linearization order that I've already shown above:
{<anonymous-local>, Incrementing, Doubling, BasicIntQueue, IntQueue, AnyRef, Any}
Everything seems to work as expected, no reason to write to Scala-Users.
The ordering of the output reflects the actual linearization, which is
Incrementing -> Doubling -> BasicIntQueue -> IntQueue -> AnyRef -> Any
Explanation
A class declaration
class C extends S with T1 with T2
is linearized to
C -> T2 -> T1 -> S
The traits come before the class S because the traits can modify the behaviour of S and must therefore be lower in the class hierarchy. Likewise a later trait can modify an earlier trait and must therefore be lower in the class hierarchy. Hence the order of the classes and traits in the linearization is the reverse of the declaration order.
Your example
Applying this to your example, the line
val incrThendoublingQueue = new BasicIntQueue with Doubling with Incrementing
is roughly the same as
class Temp extends BasicIntQueue with Doubling with Incrementing
val incrThendoublingQueue = new Temp()
Using the transformation above, Temp is linearized to
Temp -> Incrementing -> Doubling -> BasicIntQueue
This gives the class hierarchy that is implied by the output of your code.
Related
I have a program structured as:
abstract class IntQueue {
def get(): Int
def put(x: Int)
}
trait Doubling extends IntQueue{
abstract override def put(x: Int) {
println("In Doubling's put")
super.put(2*x)
}
}
trait Incrementing extends IntQueue {
abstract override def put(x: Int) {
println("In Incrementing's put")
super.put(x + 1)
}
}
class BasicIntQueue extends IntQueue {
private val buf = new ArrayBuffer[Int]
def get() = buf.remove(0)
def put(x: Int) {
println("In BasicIntQueue's put")
buf += x
}
}
When I do:
val incrThendoublingQueue = new BasicIntQueue with Doubling with
Incrementing
incrThendoublingQueue.put(10)
println(incrThendoublingQueue.get())
Output is:
In Incrementing's put
In Doubling's put
In BasicIntQueue's put
22
I am a bit confused about ordering here. My understanding of linearization order for this scenario is:
BasicIntQueue -> Incrementing -> Doubling -> IntQueue -> AnyRef -> Any
So when I call put, shouldn't BasicIntQueue's version be called first?
No. The linearization in this case is
{<anonymous-local>, Incrementing, Doubling, BasicIntQueue, IntQueue, AnyRef, Any}
The BasicIntQueue serves a the base of the mixin stack, with Doubling wrapping around it, and then Incrementing wrapping around both Basic and Doubling. So, when you call put on the entire thing, the outermost Incrementing is hit first, then the Doubling, then finally the Basic. The Basic must come last, because ultimately, it cannot delegate the actual insertion of the elements into the buffer to anyone else.
You can:
Just read the spec and convince yourself that it must be the case
Implement a toy-version of the algorithm from the spec, see what it outputs for various class definitions (it's somewhat enlightening, but mostly done for fun)
Reading the spec
The section 5.1.2 of the Spec
tells you precisely how the linearization is computed. You seem to have forgotten to reverse the indices 1 ... n in L(c_n) + ... + L(c_1).
If you apply the correct formula, you get the following linearizations for the involved traits and base classes:
IntQueue : {IntQueue, AnyRef, Any}
Doubling : {Doubling, IntQueue, AnyRef, Any}
Incrementing : {Incrementing, IntQueue, AnyRef, Any}
BasicIntQueue : {BasicIntQueue, IntQueue, AnyRef, Any}
If you then finally combine these linearizations to compute the linearization of the anonymous local class that is instantiated as incrThendoublingQueue:
<anonymous-local-class>, L(Incrementing) + L(Doubling) + L(BasicInt)
you obtain the linearization already shown above. Therefore, the methods should be invoked in this order:
incrementing
doubling
basic
which is consistent with the actual output.
Reimplementing linearization algorithm for fun
This is actually one of those dependency-free snippets of the specification that you can implement easily from scratch. The
definition of concatenation with replacement can be copied from
the spec as-is, it's almost runnable code (except that the funny plus with arrow is somewhat difficult to type, and that I wanted it as an infix operator on lists):
implicit class ConcatenationWithReplacementOps[A](list: List[A]) {
def +^->(other: List[A]): List[A] = list match {
case Nil => other
case h :: t =>
if (other contains h) (t +^-> other)
else h :: (t +^-> other)
}
}
Modeling a class declaration C extends C1 with ... with Cn is also
really simple:
case class ClassDecl(c: String, extendsTemplate: List[ClassDecl]) {
def linearization: List[String] = c :: (
extendsTemplate
.reverse
.map(_.linearization)
.foldLeft(List.empty[String])(_ +^-> _)
)
}
The formula for linearization is implemented as a method here. Notice the reverse.
The example given in the spec:
val any = ClassDecl("Any", Nil)
val anyRef = ClassDecl("AnyRef", List(any))
val absIterator = ClassDecl("AbsIterator", List(anyRef))
val richIterator = ClassDecl("RichIterator", List(absIterator))
val stringIterator = ClassDecl("StringIterator", List(absIterator))
val iter = ClassDecl("Iter", List(stringIterator, richIterator))
println(iter.linearization.mkString("{", ", ", "}"))
produces the output exactly as in the spec:
{Iter, RichIterator, StringIterator, AbsIterator, AnyRef, Any}
Now, here is a model of your example:
val intQueue = ClassDecl("IntQueue", List(anyRef))
val doubling = ClassDecl("Doubling", List(intQueue))
val incrementing = ClassDecl("Incrementing", List(intQueue))
val basicQueue = ClassDecl("BasicIntQueue", List(intQueue))
val incrThendoublingQueue = ClassDecl(
"<anonymous-local>",
List(basicQueue, doubling, incrementing)
)
println(incrThendoublingQueue.linearization.mkString("{", ", ", "}"))
It produces the linearization order that I've already shown above:
{<anonymous-local>, Incrementing, Doubling, BasicIntQueue, IntQueue, AnyRef, Any}
Everything seems to work as expected, no reason to write to Scala-Users.
The ordering of the output reflects the actual linearization, which is
Incrementing -> Doubling -> BasicIntQueue -> IntQueue -> AnyRef -> Any
Explanation
A class declaration
class C extends S with T1 with T2
is linearized to
C -> T2 -> T1 -> S
The traits come before the class S because the traits can modify the behaviour of S and must therefore be lower in the class hierarchy. Likewise a later trait can modify an earlier trait and must therefore be lower in the class hierarchy. Hence the order of the classes and traits in the linearization is the reverse of the declaration order.
Your example
Applying this to your example, the line
val incrThendoublingQueue = new BasicIntQueue with Doubling with Incrementing
is roughly the same as
class Temp extends BasicIntQueue with Doubling with Incrementing
val incrThendoublingQueue = new Temp()
Using the transformation above, Temp is linearized to
Temp -> Incrementing -> Doubling -> BasicIntQueue
This gives the class hierarchy that is implied by the output of your code.
I'm trying to add new functions to existing types (so I can have the IDE auto suggest relevant functions for types I don't have control over, eg Future[Option[A]]). I've explored both implicit classes and implicit conversions to accomplish this and they both seem to offer the same behavior.
Is there any effective difference between using an implicit class:
case class Foo(a: Int)
implicit class EnrichedFoo(foo: Foo) {
def beep = "boop"
}
Foo(1).beep // "boop"
And using an implicit conversion:
case class Foo(a: Int)
trait Enriched {
def beep: String
}
implicit def fooToEnriched(foo: Foo) = new Enriched {
def beep = "boop"
}
Foo(1).beep // "boop"
I suppose one difference here might be that the first example creates a one-off class instead of a trait, but I could easily adapt the implicit class to extend an abstract trait, eg:
case class Foo(a: Int)
trait Enriched {
def beep: String
}
implicit class EnrichedFoo(foo: Foo) extends Enriched {
def beep = "boop"
}
Foo(1).beep // "boop"
As far as I'd know, they're pretty much exactly the same. The scoping rules also equally apply to both.
In my opinion, I'd use the implicit classes for your kind of situation. They were probably created exactly for something like that.
Implicit conversions, to me, are more appropriate when you already actually have two different kind of classes and want to convert between the two.
You can check out the initial proposal for implicit classes right here.
There it says:
A new language construct is proposed to simplify the creation of classes which provide extension methods to another type.
You can even see how it desugars implicit classes. The following:
implicit class RichInt(n: Int) extends Ordered[Int] {
def min(m: Int): Int = if (n <= m) n else m
...
}
will desugar into:
class RichInt(n: Int) extends Ordered[Int] {
def min(m: Int): Int = if (n <= m) n else m
...
}
implicit final def RichInt(n: Int): RichInt = new RichInt(n)
Well to me its a matter of preference. Actually the implicit classes came into being to ease the creation of classes which provide extension methods to another type.
Implicit classes add a lot of value to value classes though.
To add on Luka Jacobowitz answer: Implicit classes are basically extensions. Implicit conversion is used to tell the compiler that it may be treated as something with extension.
Sounds nearly the same. Two things of interest from implicit conversion to have some difference:
First: You may need to activate the language feature for disabling warnings when using implicit conversion.
Second: The term of "converting" the type may be confusing:
Implicit conversions are applied in two situations:
If an expression e is of type S, and S does not conform to the expression’s expected type T.
[Or:] In a selection e.m with e of type S, if the selector m does not denote a member of S.
case class Foo(a: Int)
trait Enriched {
def beep: String
}
implicit def fooToEnriched(foo: Foo) = new Enriched {
def beep = "boop"
}
Foo(1) match {
case _:Enriched => println("is an Enriched")
case _:Foo => println("no, was a Foo")
}
// no, was a Foo
but it may be treated as an Enriched...
val enriched: Enriched = Foo(2)
enriched match {
case _:Enriched => println("is an Enriched")
case _:Foo => println("no, was a Foo")
}
// is an Enriched
// plus unreachable code warning: case _:Foo => println("no, was a Foo")
I have a simple trait that requires the implementation to have a method quality(x:A) which I want to return an Ordered[B]. In other words, quality transforms A to Ordered[B]. Such that I can compare to B's.
I have the following basic code:
trait Foo[A] {
def quality[B](x:A):Ordered[B]
def baz(x1:A, x2:A) = {
// some algorithm work here, and then
if (quality(x1) > quality(x2)) {
// some stuff
}
}
Which I want to implement like follows:
class FooImpl extends Foo[Bar] {
def quality(x:Bar):Double = {
someDifficultQualityCalculationWhichReturnsADouble
}
}
I figured this could work because Double is implicitly converted to RichDouble which implements Ordered[Double] if I am correct.
But at the > in the baz method of the trait it gives me an error of quality(x2) stating: Type mismatch, expected Nothing, actual Ordered[Nothing]
I do not understand this, because, coming from C#, I find it comparable to returning something like IEnumerable<A> and then using al the nice extension methods of an IEnumerable.
What am I missing here? What I want to to with the trait is define a complex algorithm inside the trait, but the key functions need to be defined by the class implementing the trait. On of these functions is needed to calculate a quality factor. This can be a Double, Int or whatnot but it can also be something more sophisticated. I could rewrite it that it always returns Double and that is certainly possible, but I want the trait to be as generic as possible because I want it to describe behavior and not implementation. I thought about the class A implementing Ordered[A] but that also seems weird, because it is not the 'purpose' of this class to be compared.
Using Ordering[A] you can compare As without requiring A to implement Ordered[A].
We can request that an Ordering[A] exists in baz by adding an implicit parameter :
trait Foo[A] {
def baz(x1:A, x2:A)(implicit ord: Ordering[A]) =
if (ord.gt(x1, x2)) "first is bigger"
else "first is smaller or equal"
}
Lets create a Person case class with an Ordering in its companion object.
case class Person(name: String, age: Int)
object Person {
implicit val orderByAge = Ordering.by[Person, Int](_.age)
}
We can now use Foo[Person].baz because an Ordering[Person] exists :
val (alice, bob) = (Person("Alice", 50), Person("Bob", 40))
val foo = new Foo[Person] {}
foo.baz(alice, bob)
// String = first is bigger
// using an explicit ordering
foor.baz(alice, bob)(Ordering.by[Person, String](_.name))
// String = first is smaller or equal
In the same manner as I compared Persons by age, you could create an Ordering[A] to compare your A by your quality function.
To complement Peter's answer: in Scala we have two traits: Ordering[T] and Ordered[A]. You should use them in different situations.
Ordered[A] is for cases when a class you implement can be ordered naturally and that order is the only one.
Example:
class Fraction(val numerator: Int, val denominator: Int) extends Ordered[Fraction]
{
def compare(that: Fraction) = {
(this.numerator * that.denominator) compare (this.denominator * that.numerator)
}
}
Ordering[T] is for cases when you want to have different ways to order things. This way the strategy of defining the order can be decoupled from the class being ordered.
For an example I will borrow Peter's Person:
case class Person(name: String, age: Int)
object PersonNameOrdering extends Ordering[Person]
{
def compare(x: Person, y: Person) = x.name compare y.name
}
Note, that since PersonNameOrdering doesn't have any instance fields, all it does is encapsulate the logic of defining an order of two Person's. Thus I made it an object rather than a class.
To cut down the boilerplate you can use Ordering.on to define an Ordering:
val personAgeOrdering: Ordering[Person] = Ordering.on[Person](_.age)
Now to the fun part: how to use all this stuff.
In your original code Foo[A].quantity was indirectly defining a way to order your A's. Now to make it idiomatic Scala let's use Ordering[A] instead, and rename quantity to ord:
trait Foo[A] {
def baz(x1: A, x2: A, ord: Ordering[A]) = {
import ord._
if (x1 > x2) "first is greater"
else "first is less or equal"
}
}
Several things to note here:
import ord._ allows to use infix notation for comparisons, i.e. x1 > x2 vs ord.gt(x1, x2)
baz is now parametrized by ordering, so you can dynamically choose how to order x1 and x2 on a case-by-case basis:
foo.baz(person1, person2, PersonNameOrdering)
foo.baz(person1, person2, personAgeOrdering)
The fact that ord is now an explicit parameter can sometimes be inconvenient: you may not want to pass it explicitly all the time, while there might be some cases when you want to do so. Implicits to the rescue!
def baz(x1: A, x2: A) = {
def inner(implicit ord: Ordering[A]) = {
import ord._
if (x1 > x2) "first is greater"
else "first is less or equal"
}
inner
}
Note the implicit keyword. It is used to tell the compiler to draw the parameter from the implicit scope in case you don't provide it explicitly:
// put an Int value to the implicit scope
implicit val myInt: Int = 5
def printAnInt(implicit x: Int) = { println(x) }
// explicitly pass the parameter
printAnInt(10) // would print "10"
// let the compiler infer the parameter from the implicit scope
printAnInt // would print "5"
You might want to learn where does Scala look for implicits.
Another thing to note is the need of a nested function. You cannot write def baz(x1: A, x2: A, implicit ord: Ordering[A]) - that would not compile, because the implicit keyword applies to the whole parameter list.
In order to cope with this little problem baz was rewritten in such a clunky way.
This form of rewritting turned out to be so common that a nice syntactic sugar was introduced for it - multiple parameter list:
def baz(x1: A, x2: A)(implicit ord: Ordering[A]) = {
import ord._
if (x1 > x2) "first is greater"
else "first is less or equal"
}
The need of an implicit parametrized by a type is also quite common so the code above can be rewritten with even more sugar - context bound:
def baz[A: Ordering](x1: A, x2: A) = {
val ord = implicitly[Ordering[A]]
import ord._
if (x1 > x2) "first is greater"
else "first is less or equal"
}
Please bear in mind that all these transformations of baz function are nothing but syntactic sugar application. So all the versions are exactly the same and compiler would desugarize each of the versions to the same bytecode.
To recap:
extract the A ordering logic from quantity function to the Ordering[A] class;
put an instance of Ordering[A] to the implicit scope or pass the ordering explicitly depending on your needs;
pick "your flavor" of syntactic sugar for baz: no sugar/nested functions, multiple parameter list or context bound.
UPD
To answer the original question "why doesn't it compile?" let me start from a little digression on how infix comparison operator works in Scala.
Given the following code:
val x: Int = 1
val y: Int = 2
val greater: Boolean = x > y
Here's what actually happens. Scala doesn't have infix operators per se, instead infix operators are just a syntactic sugar for single parameter method invocation. So internally the code above transforms to this:
val greater: Boolean = x.>(y)
Now the tricky part: Int doesn't have an > method on its own. Pick ordering by inheritance on the ScalaDoc page and check that this method is listed in a group titled "Inherited by implicit conversion intWrapper from Int to RichInt".
So internally compiler does this (well, except that for performance reasons that there is no actual instantiation of an extra object on heap):
val greater: Boolean = (new RichInt(x)).>(y)
If we proceed to ScalaDoc of RichInt and again order methods by inheritance it turns out that the > method actually comes from Ordered!
Let's rewrite the whole block to make it clearer what actually happens:
val x: Int = 1
val y: Int = 2
val richX: RichInt = new RichInt(x)
val xOrdered: Ordered[Int] = richX
val greater: Boolean = xOrdered.>(y)
The rewriting should have highlighted the types of variables involved in comparison: Ordered[Int] on the left and Int on the right. Refer > documentation for confirmation.
Now let's get back to the original code and rewrite it the same way to highlight the types:
trait Foo[A] {
def quality[B](x: A): Ordered[B]
def baz(x1: A, x2: A) = {
// some algorithm work here, and then
val x1Ordered: Ordered[B] = quality(x1)
val x2Ordered: Ordered[B] = quality(x2)
if (x1Ordered > x2Ordered) {
// some stuff
}
}
}
As you can see the types do not align: they are Ordered[B] and Ordered[B], while for > comparison to work they should have been Ordered[B] and B respectively.
The question is where do you get this B to put on the right? To me it seems that B is in fact the same as A in this context. Here's what I came up with:
trait Foo[A] {
def quality(x: A): Ordered[A]
def baz(x1: A, x2: A) = {
// some algorithm work here, and then
if (quality(x1) > x2) {
"x1 is greater"
} else {
"x1 is less or equal"
}
}
}
case class Cargo(weight: Int)
class CargoFooImpl extends Foo[Cargo] {
override def quality(x: Cargo): Ordered[Cargo] = new Ordered[Cargo] {
override def compare(that: Cargo): Int = x.weight compare that.weight
}
}
The downside of this approach is that it is not obvious: the implementation of quality is too verbose and quality(x1) > x2 is not symmetrical.
The bottom line:
if you want the code to be idiomatic Scala go for Ordering[T]
if you don't want to mess with implicits and other Scala magic implement quality as quality(x: A): Double for all As; Doubles are good and generic enough to be compared and ordered.
Specialization promises to provide high-efficiency implmentations for primitive types
with minimal extra boilerplate. But specialization seems to be too eager for its own good.
If I want to specialize a class or method,
def foo[#specialized(Byte) A](a: A): String = ???
class Bar[#specialized(Int) B] {
var b: B = ???
def baz: B = ???
}
then I am required to write a single implementation that covers both the specialized and the generic cases.
What if those cases are really different from each other, such that the implementations do not overlap?
For example, if I wanted to perform math on bytes, I would need to insert a bunch of & 0xFFs into the
logic.
I could possibly write a specialized type-class to do the math right, but doesn't that just push the same
problem back one level? How do I write my specialized + method for that type class in a way that doesn't
conflict with a more general implementation?
class Adder[#specialized(Byte) A] {
def +(a1: A, a2: A): A = ???
}
Also, once I do create a type-class this way, how do I make sure the correct type class is used for my specialized methods
instead of the general version (which, if it is truly general, should probably compile and certainly would run, except that it isn't what I want)?
Is there a way to do this without macros? Is it easier with macros?
This is my best attempt so far. It works but the implementation isn't pretty (even if the results are). Improvements are welcome!
There is a macro-free way to do this, both at the class and method level, and it does involve type classes--quite a lot of
them! And the answer is not exactly the same for classes and methods. So bear with me.
Manually Specialized Classes
You manually specialize classes the same way that you manually provide any kind of different implementation for classes:
your superclass is abstract (or is a trait), and the subclasses provide the implementation details.
abstract class Bippy[#specialized(Int) B] {
def b: B
def next: Bippy[B]
}
class BippyInt(initial: Int) extends Bippy[Int] {
private var myB: Int = initial
def b: Int = myB
def next = { myB += 1; this }
}
class BippyObject(initial: Object) extends Bippy[Object] {
private var myB: Object = initial
def b: B = myB
def next = { myB = myB.toString; this }
}
Now, if only we had a specialized method to pick out the right implementations, we'd be done:
object Bippy{
def apply[#specialized(Int) B](initial: B) = ??? // Now what?
}
So we've converted our problem of providing custom specialized classes and methods into just
needing to provide custom specialized methods.
Manually Specialized Methods
Manually specializing a method requires a way to write one implementation that can nonetheless
select which implementation you want (at compile time). Type classes are great at this. Suppose
we already had type classes that implemented all of our functionality, and that the compiler would
select the right one. Then we could just write
def foo[#specialized(Int) A: SpecializedFooImpl](a: A): String =
implicitly[SpecializedFooImpl[A]](a)
...or we could if implicitly was guaranteed to preserve specialization and if we only
ever wanted a single type parameter. In general these things are not true, so we'll write
our type class out as an implicit parameter rather than relying on the A: TC syntactic sugar.
def foo[#specialized(Int) A](a: A)(implicit impl: SpecializedFooImpl[A]): String =
impl(a)
(Actually, that's less boilerplate anyway.)
So we've converted our problem of providing custom specialized methods into just needing
to write specialized typeclasses and getting the compiler to fill in the correct ones.
Manually Specialized Type Classes
Type classes are just classes, and now we have to write specialized classes again, but
there's a critical difference. The user isn't the one asking for arbitrary instances.
This gives us just enough extra flexibility for it to work.
For foo, we need an Int version and a fully generic version.
trait SpecFooImpl[#specialized (Int), A] {
def apply(param: A): String
}
final class SpecFooImplAny[A] extends SpecFooImpl[A] {
def apply(param: A) = param.toString
}
final class SpecFooImplInt extends SpecFooImpl[Int] {
def apply(param: Int) = "!" * math.max(0, param)
}
Now we could create implicits to supply those type classes like so
implicit def specFooAsAny[A] = new SpecFooImplAny[A]
implicit val specFooAsInt = new SpecFooImplInt
except we have a problem: if we actually try to call foo: Int, both implicits will apply.
So if we just had a way to prioritize which type class we chose, we'd be all set.
Selection of type classes (and implicits in general)
One of the secret ingredients the compiler uses to determine the right implicit to use
is inheritance. If implicits come from A via B extends A, but B
declares its own that also could apply, those in B win if all else is equal.
So we put the ones we want to win deeper in the inheritance hierarchy.
Also, since you're free to define implicits in traits, you can mix them in anywhere.
So the last piece of our puzzle is to pop our type class implicits into a chain
of traits that extend each other, with the more generic ones appearing earlier.
trait LowPriorityFooSpecializers {
implicit def specializeFooAsAny[A] = new SpecializedFooImplAny[A]
}
trait FooSpecializers extends LowPriorityFooSpecializers {
implicit val specializeFooAsInt = new SpecializedFooImplInt
}
Mix in the highest-priority trait to wherever the implicits are needed, and the
type classes will be picked as desired.
Note that the type classes will be as specialized as you make them even if the
specialized annotation is not used. So you can do without specialized at all,
as long as you know the type precisely enough, unless you want to use specialized
functions or interoperate with other specialized classes. (And you probably do.)
A complete example
Let's suppose we want to make a two-parameter specialized bippy function that
will do apply the following transformation:
bippy(a, b) -> b
bippy(a, b: Int) -> b+1
bippy(a: Int, b) -> b
bippy(a: Int, b: Int) -> a+b
We should be able to achieve this with three type classes and a single specialized
method. Let's try, first the method:
def bippy[#specialized(Int) A, #specialized(Int) B](a: A, b: B)(implicit impl: SpecBippy[A, B]) =
impl(a, b)
Then the type classes:
trait SpecBippy[#specialized(Int) A, #specialized(Int) B] {
def apply(a: A, b: B): B
}
final class SpecBippyAny[A, B] extends SpecBippy[A, B] {
def apply(a: A, b: B) = b
}
final class SpecBippyAnyInt[A] extends SpecBippy[A, Int] {
def apply(a: A, b: Int) = b + 1
}
final class SpecBippyIntInt extends SpecBippy[Int, Int] {
def apply(a: Int, b: Int) = a + b
}
Then the implicits in chained traits:
trait LowerPriorityBippySpeccer {
// Trick to avoid allocation since generic case is erased anyway!
private val mySpecBippyAny = new SpecBippyAny[AnyRef, AnyRef]
implicit def specBippyAny[A, B] = mySpecBippyAny.asInstanceOf[SpecBippyAny[A, B]]
}
trait LowPriorityBippySpeccer extends LowerPriorityBippySpeccer {
private val mySpecBippyAnyInt = new SpecBippyAnyInt[AnyRef]
implicit def specBippyAnyInt[A] = mySpecBippyAnyInt.asInstanceOf[SpecBippyAnyInt[A]]
}
// Make this last one an object so we can import the contents
object BippySpeccer extends LowPriorityBippySpeccer {
implicit val specBippyIntInt = new SpecBippyIntInt
}
and finally we'll try it out (after pasting everything in together in :paste in the REPL):
scala> import Speccer._
import Speccer._
scala> bippy(Some(true), "cod")
res0: String = cod
scala> bippy(1, "salmon")
res1: String = salmon
scala> bippy(None, 3)
res2: Int = 4
scala> bippy(4, 5)
res3: Int = 9
It works--our custom implementations are enabled. Just to check that we can use
any type, but we don't leak into the wrong implementation:
scala> bippy(4, 5: Short)
res4: Short = 5
scala> bippy(4, 5: Double)
res5: Double = 5.0
scala> bippy(3: Byte, 2)
res6: Int = 3
And finally, to verify that we have actually avoided boxing, we'll time bippy at
summing a bunch of integers:
scala> val th = new ichi.bench.Thyme
th: ichi.bench.Thyme = ichi.bench.Thyme#1130520d
scala> val adder = (i: Int, j: Int) => i + j
adder: (Int, Int) => Int = <function2>
scala> var a = Array.fill(1024)(util.Random.nextInt)
a: Array[Int] = Array(-698116967, 2090538085, -266092213, ...
scala> th.pbenchOff(){
var i, s = 0
while (i < 1024) { s = adder(a(i), s); i += 1 }
s
}{
var i, s = 0
while (i < 1024) { s = bippy(a(i), s); i += 1 }
s
}
Benchmark comparison (in 1.026 s)
Not significantly different (p ~= 0.2795)
Time ratio: 0.99424 95% CI 0.98375 - 1.00473 (n=30)
First 330.7 ns 95% CI 328.2 ns - 333.1 ns
Second 328.8 ns 95% CI 326.3 ns - 331.2 ns
So we can see that our specialized bippy-adder achieves the same kind of performance
as specialized Function2 does (about 3 adds per ns, which is about right for a modern
machine).
Summary
To write custom specialized code using the #specialized annotation,
Make the specialized class abstract and manually supply concrete implementations
Make specialized methods (including generators for a specialized class) take typeclasses that do the real work
Make the base typeclass trait #specialized and provide concrete implementations
Provide implicit vals or defs in an inheritance-hierarchy of traits so the correct one is selected
It's a lot of boilerplate, but at the end of it all you get a seamless custom-specialized experience.
This is an answer from the scala internals mailing list:
With miniboxing specialization, you can use the reflection feature:
import MbReflection._
import MbReflection.SimpleType._
import MbReflection.SimpleConv._
object Test {
def bippy[#miniboxed A, #miniboxed B](a: A, b: B): B =
(reifiedType[A], reifiedType[B]) match {
case (`int`, `int`) => (a.as[Int] + b.as[Int]).as[B]
case ( _ , `int`) => (b.as[Int] + 1).as[B]
case (`int`, _ ) => b
case ( _ , _ ) => b
}
def main(args: Array[String]): Unit = {
def x = 1.0
assert(bippy(3,4) == 7)
assert(bippy(x,4) == 5)
assert(bippy(3,x) == x)
assert(bippy(x,x) == x)
}
}
This way, you can choose the exact behavior of the bippy method based on the type arguments without defining any implicit classes.
I know it's quite old, but I bumped at it looking for something else and maybe it'll prove useful. I had a similar motivation, and answered it in how to check I'm inside a specialized function or class
I used a reverse lookup table - SpecializedKey is a specialized class which equals all other instances with the same specialization, so I can perform a check like this
def onlyBytes[#specialized E](arg :E) :Option[E] =
if (specializationFor[E]==specializationFor[Byte]) Some(arg)
else None
Of course, there's no performance benefit when working with individual primitive values, but with collections, especially iterators, it becomes useful.
final val AllButUnit = new Specializable.Group((Byte, Short, Int, Long, Char, Float, Double, Boolean, AnyRef))
def specializationFor[#specialized(AllButUnit) E] :ResolvedSpecialization[E] =
Specializations(new SpecializedKey[E]).asInstanceOf[ResolvedSpecialization[E]]
private val Specializations = Seq(
resolve[Byte],
resolve[Short],
resolve[Int],
resolve[Long],
resolve[Char],
resolve[Float],
resolve[Double],
resolve[Boolean],
resolve[Unit],
resolve[AnyRef]
).map(
spec => spec.key -> spec :(SpecializedKey[_], ResolvedSpecialization[_])
).toMap.withDefaultValue(resolve[AnyRef])
private def resolve[#specialized(AllButUnit) E :ClassTag] :ResolvedSpecialization[E] =
new ResolvedSpecialization[E](new SpecializedKey[E], new Array[E](0))
class ResolvedSpecialization[#specialized(AllButUnit) E] private[SpecializedCompanion]
(val array :Array[E], val elementType :Class[E], val classTag :ClassTag[E], private[SpecializedCompanion] val key :SpecializedKey[E]) {
private[SpecializedCompanion] def this(key :SpecializedKey[E], array :Array[E]) =
this(array, array.getClass.getComponentType.asInstanceOf[Class[E]], ClassTag(array.getClass.getComponentType.asInstanceOf[Class[E]]), key)
override def toString = s"#specialized($elementType)"
override def equals(that :Any) = that match {
case r :ResolvedSpecialization[_] => r.elementType==elementType
case _ => false
}
override def hashCode = elementType.hashCode
}
private class SpecializedKey[#specialized(AllButUnit) E] {
override def equals(that :Any) = that.getClass==getClass
override def hashCode = getClass.hashCode
def className = getClass.getName
override def toString = className.substring(className.indexOf("$")+1)
}
Is it possible to generically replace arguments in a case class? More specifically, say I wanted a substitute function that received a "find" case class and a "replace" case class (like the left and right sides of a grammar rule) as well as a target case class, and the function would return a new case class with arguments of the find case class replaced with the replace case class? The function could also simply take a case class (Product?) and a function to be applied to all arguments/products of the case class.
Obviously, given a specific case class, I could use unapply and apply -- but what's the best/easiest/etc way to generically (given any case class) write this sort of function?
I'm wondering if there is a good solution using Scala 2.10 reflection features or Iso.hlist from shapeless.
For example, what I really want to be able to do is, given classes like the following...
class Op[T]
case class From(x:Op[Int]) extends Op[Int]
case class To(x:Op[Int]) extends Op[Int]
case class Target(a:Op[Int], b:Op[Int]) extends ...
// and lots of other similar case classes
... have a function that can take an arbitrary case class and return a copy of it with any elements of type From replaced with instances of type To.
If you'll pardon the plug, I think you'll find that the rewriting component of our Kiama language processing library is perfect for this kind of purpose. It provides a very powerful form of strategic programming.
Here is a complete solution that rewrites To's to From's in a tree made from case class instances.
import org.kiama.rewriting.Rewriter
class Op[T]
case class Leaf (i : Int) extends Op[Int]
case class From (x : Op[Int]) extends Op[Int]
case class To (x : Op[Int]) extends Op[Int]
case class Target1 (a : Op[Int], b : Op[Int]) extends Op[Int]
case class Target2 (c : Op[Int]) extends Op[Int]
object Main extends Rewriter {
def main (args : Array[String]) {
val replaceFromsWithTos =
everywhere {
rule {
case From (x) => To (x)
}
}
val t1 = Target1 (From (Leaf (1)), To (Leaf (2)))
val t2 = Target2 (Target1 (From (Leaf (3)), Target2 (From (Leaf (4)))))
println (rewrite (replaceFromsWithTos) (t1))
println (rewrite (replaceFromsWithTos) (t2))
}
}
The output is
Target1(To(Leaf(1)),To(Leaf(2)))
Target2(Target1(To(Leaf(3)),Target2(To(Leaf(4)))))
The idea of the replaceFromsWithTos value is that the rule construct lifts a partial function to be able to operate on any kind of value. In this case the partial function is only defined at From nodes, replacing them with To nodes. The everywhere combinator says "apply my argument to all nodes in the tree, leaving unchanged places where the argument does not apply.
Much more can be done than this kind of simple rewrite. See the main Kiama rewriting documentation for the gory detail, including links to some more examples.
I experimented a bit with shapeless and was able to come up with the following, relatively generic way of converting one case class into another:
import shapeless._ /* shapeless 1.2.3-SNAPSHOT */
case class From(s: String, i: Int)
case class To(s: String, i: Int)
implicit def fromIso = Iso.hlist(From.apply _, From.unapply _)
implicit def toIso = Iso.hlist(To.apply _, To.unapply _)
implicit def convert[A, B, L <: HList]
(a: A)
(implicit srcIso: Iso[A, L],
dstIso: Iso[B, L])
: B =
dstIso.from(srcIso.to(a))
val f1 = From("Hi", 7)
val t1 = convert(f1)(fromIso, toIso)
println("f1 = " + f1) // From("Hi", 7)
println("t1 = " + t1) // To("Hi", 7)
However, I was not able to get the implicits right. Ideally,
val t1: To = f1
would be sufficient, or maybe
val t1 = convert(f1)
Another nice improvement would be to get rid of the need of having to explicitly declare iso-implicits (fromIso, toIso) for each case class.
I don't think you'll really find a better way than just using unapply/apply through pattern matching:
someValue match {
case FindCaseClass(a, b, c) => ReplaceCaseClass(a, b, c)
// . . .
}
You have to write out the rules to associate FindCaseClass with ReplaceCaseClass somehow, and although you might be able to do it a little more succinctly by somehow just using the names, this has the added benefit of also checking the number and types of the case class fields at compile time to make sure everything matches just right.
There is probably some way to do this automatically using the fact that all case classes extend Product, but the fact that productElement(n) returns Any might make it a bit of a pain—I think that's where reflection would have to come in. Here's a little something to get you started:
case class From(i: Int, s: String, xs: Seq[Nothing])
case class To(i: Int, s: String, xs: Seq[Nothing])
val iter = From(5,"x",Nil).productIterator
val f = To.curried
iter.foldLeft(f: Any) { _.asInstanceOf[Any => Any](_) }
// res0: Any = To(5,x,List())
But really, I think you're better off with the pattern-matching version.
Edit: Here is a version with the relavent code refactored into a method:
case class From(i: Int, s: String, xs: Seq[Nothing])
case class To(i: Int, s: String, xs: Seq[Nothing])
type Curryable = { def curried: _ => _ }
def recase(from: Product, to: Curryable) = {
val iter = from.productIterator
val f = to.curried
iter.foldLeft(f: Any) { _.asInstanceOf[Any => Any](_) }
}
recase(From(5,"x",Nil), To)
// res0: Any = To(5,x,List())