I'm coming from C++ and trying to wrap my head around scala's type system.
Consider the following C++ template class:
template<class T>
class Point2
{
Point2( T x, T y ) :
x(x),
y(y)
{}
T x;
T y;
Point2<T> operator+( Point<T> const& other ) const
{
return Point<T>(x+other.x, y+other.y);
}
T sumComponents() const { return x+y; }
}
Point2<Double> p0(12.3, 45.6)
Point2<Double> p1(12.3, 45.6)
Point2<Double> p = p1+p2
Double d = p1.sumComponents()
I'm finding I want to write something like this:
case class Point2[ T ] (x:T, y:T) {
def +() Point2[T]: = x+y
def sumComponents() T: = x+y
}
or, (because the compile has problems with this),
trait Addable[T] { // Require T supports the + operatory
def +( that:T ):T
}
case class Point2[ T<:Addable[T] ] (x:T, y:T) {
def +() Point2[T]: = x+y
def sumComponents() T: = x+y
}
which is similarly problematic because I can't require Double to extend Addable.
Generally, I'm finding it scala's type system works with a set of constraints that I don't quite understand.
What's the idiomatic way of implementing the above in scala?
And what's the right way for C++ template programmers to understand the limits of generics in scala? (why can't I do this in scala? e.g. Is it because generics are compiled before being instaniated?)
What's the idiomatic way of implementing the above in scala?
Either by specifying appropriate requirements of T, or using type classes to provide the desired behavior. I'll get back to this later.
And what's the right way for C++ template programmers to understand
the limits of generics in scala? (why can't I do this in scala? e.g.
Is it because generics are compiled before being instaniated?)
C++ templates are compiled "at" the usage site, and different code is generated for each combination of parameters to the templates. So if you use the class above with int and double, you get two different Point2 classes compiled.
Basically, C++ templates are macros, though nowhere near as dumb as #define macros. In fact, C++ templates are turing complete. Maybe it will be possible to accomplish something equivalent in the future, with the upcoming macro capabilities planned for Scala 2.11 and beyond, but let's ignore that for now.
Type parameters (Scala equivalent of Java generics) do not change how a code is compiled. A parameterized class generates its bytecode when it is compiled, not when it is used. So, by the time one instantiates a Point2 with Double, it is too late to generate bytecode.
That means that the code generated by a parameterized class must be compatible with all types a class can be instantiated with.
And that's the source of the trouble: any methods called on T must be known to be present on T at the time Point2 is compiled. Therefore, T must be defined to have an upper boundary of traits or classes that define such methods, as you have shown in your example.
Of course, that is not always possible, as you rightly pointed out, and that's where type classes come in. A type class is a set of types for which a set of behaviors is defined. Type classes, as implemented in Scala, are defined as classes whose instances define the behavior of other classes.
In the example you gave, you'd be using either the Numeric type class, or the Fractional type class if you need fractional division as well. A simple example of type class use is:
scala> import scala.math.Numeric
import scala.math.Numeric
scala> def sum[T](x: T, y: T)(implicit num: Numeric[T]): T = num.plus(x, y)
sum: [T](x: T, y: T)(implicit num: scala.math.Numeric[T])T
Or, using a special notation called "context bounds",
scala> def sum[T : Numeric](x: T, y: T): T = implicitly[Numeric[T]].plus(x, y)
sum: [T](x: T, y: T)(implicit evidence$1: scala.math.Numeric[T])T
The notation T : Numeric can be read as T such that there's an implicit instance of Numeric[T] available. The code implicitly[X] returns an implicit value of type X if one can be found (or fails at compile time).
Now, notice how no method is called on x and y -- instead, we call methods on num whose class is Numeric[T]. The class Numeric[T] has a method plus which knows how to add two Ts.
Because what we need is type class instances, one can easily add new types to satisfy a type class. One could easily declare a Numeric type class for Point2 (assuming all its methods could be implemented):
class Point2Numeric[T](implicit num: Numeric[T]) extends Numeric[Point2[T]] {
def plus(x: Point2[T], y: Point2[T]): Point2[T] = x + y
// etc
}
implicit def ToPoint2Numeric[T : Numeric] = new Point2Numeric[T]
With that in place, then for any T for which there's a Numeric[T], there would be also a Numeric[Point2[T]].
After plain type inheritance (upper type bounds), type classes are the most common form of type constraint used in Scala. There are other forms a bit more sophisticated, for which there's some discussion whether they are type classes or something different -- the magnet pattern, for instance. Look at shapeless for an example of how far one can take such things.
Another kind of type constraint that used to be very common but is now being used more circumspectly are view bounds. I'll not go into details (in fact, search for context bounds and view bounds to find a long answer about it from myself), but they can be used to make type classes more readable when used. For example:
scala> import scala.math.Numeric.Implicits._
import scala.math.Numeric.Implicits._
scala> def sum[T : Numeric](x: T, y: T): T = x + y
sum: [T](x: T, y: T)(implicit evidence$1: scala.math.Numeric[T])T
The imported definitions contain implicit conversions that make it possible to use values of type T for which there's a Numeric[T] as if they, themselves, had the methods like + or -.
As a final note, it is important to realize this goes through many levels of indirection and, therefore, might not be very suitable for high performance code.
Simply, you can do something like this:
scala> :paste
// Entering paste mode (ctrl-D to finish)
import math.Numeric
import math.Numeric.Implicits._
case class Point2[A: Numeric](x: A, y: A) {
def + (other: Point2[A]): Point2[A] =
Point2(this.x + other.x, this.y + other.y)
def sumComponents: A = x + y
}
// Exiting paste mode, now interpreting.
import math.Numeric
import math.Numeric.Implicits._
defined class Point2
scala> val p1 = Point2(1, 2)
p1: Point2[Int] = Point2(1,2)
scala> val p2 = Point2(3, 4)
p2: Point2[Int] = Point2(3,4)
scala> p1 + p2
res2: Point2[Int] = Point2(4,6)
scala> val p3 = Point2(1.2, 3.4)
p3: Point2[Double] = Point2(1.2,3.4)
scala> val p4 = Point2(1.6, 6.4)
p4: Point2[Double] = Point2(1.6,6.4)
scala> p3 + p4
res3: Point2[Double] = Point2(2.8,9.8)
scala>
I created a library template.scala. You can use the library to create C++ flavored templates, avoiding complicated implicits.
import com.thoughtworks.template
case class Point2[T](x:T, y:T) {
#template def +(rhs: Point2[_]) = Point2(x + rhs.x, y + rhs.y)
#template def sumComponents() = x + y
}
println(Point2(1, 3).sumComponents()) // Output: 4
println(Point2(1, 3) + Point2(100, 200)) // Output: Point2(101,203)
Note that you can even plus two Point2s with different component types.
println(Point2(1.5, 0.3) + Point2(100, 200)) // Output: Point2(101.5,200.3)
Even nested Point2:
// Output: Point2(Point2(10.1,20.2),Point2(101.0,202.0))
println(Point2(Point2(0.1, 0.2), Point2(1.0, 2.0)) + Point2(Point2(10, 20), Point2(100, 200)))
It works because #template functions are code templates that will inline at the call site.
This requires a type class (which I'm calling Addition) and an implicit conversion (which I'm defining via an implicit class called Op). In practice, you would use the Numeric type for this particular situation, but for the purpose of illustration, this is how you would define your own:
trait Addition[T] {
def add(a: T, b: T): T
implicit class Op(a: T) {
def +(b: T) = add(a, b)
}
}
implicit object IntAddition extends Addition[Int] {
def add(a: Int, b: Int) = a + b
}
implicit object DoubleAddition extends Addition[Double] {
def add(a: Double, b: Double) = a + b
}
case class Point2[T](x: T, y: T)(implicit addition: Addition[T]) {
import addition.Op
def +(p: Point2[T]): Point2[T] = Point2(x + p.x, y + p.y)
def sumComponents(): T = x + y
}
Use Numeric, available as an implicit.
import scala.math.Numeric;
case class Point2[T](x: T, y: T)(implicit num: Numeric[T])
Look at Numeric in the API, does what you need.
Related
I'm learning Scala and in a book that I'm reading (Functional Programming in Scala) I came across an example of a custom List implementation in Scala which goes like this:
sealed trait MyList[+A]
case object MyNil extends MyList[Nothing]
case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A]
object MyList {
def apply[A](as: A*): MyList[A] =
if (as.isEmpty) MyNil
else Cons(as.head, apply(as. tail: _*))
}
I would like to extend MyList to add the following functionality:
add a tail method that returns all elements of a MyList instance without the first one, e.g. val x = MyList(1,2,3); x.tail == MyList(2,3).
Add a sum method that is only applicable when MyList contains Ints (or even better for all numeric types). So e.g. val x = MyList(1,2,3); x.sum == 6
The idea above 2 questions is to understand: (1) how to interact with the instance of my class and (2) how to use polymorphism in a situation like this. After some searching around, I'm not even sure how to begin with these problems, which is why I'm asking this question.
Any tips would be appreciated. Many thanks!
UPDATE:
A few updates:
First, I'd like to point out that the solution to the programming challenges in the Functional Programming course that I mentioned earlier can be found here, however, I'm looking for something a little different than what the author is asking for.
I've managed to find an answer to my first question "how can I use tail on my instance itself, e.g. MyList(1,2,3).tail?". To solve this, I had to modify the original trait in the following manner:
sealed trait MyList[+A] {
def tail: MyList[A] = MyList.tail(this)
}
I'm not sure if this is the best way of doing what I want to do, but it works. If anyone has better suggestions, please let me know.
The second part is harder. I wanted to add the following inside the same trait:
def sum[Int]: MyList[Int] = MyList.sum(this)
But IntelliJ is complaining about the type of this which is A and I need to apply this conditionally on this being of type Int.
Another alternative is to do the following:
def sum: Int = this match {
case x: MyList[Int] => MyList.sum(x)
}
But what if we want to create another implementation for String that will also return a String? This cannot be the right solution and I haven't found one yet. Please help :)
.tail
I note that your Cons class already has a public tail member. I'd be tempted to start there and just make it universal...
sealed trait MyList[+A] {
def tail: MyList[A]
}
...and add the MyNil implementation.
case object MyNil extends MyList[Nothing] {
def tail: MyList[Nothing] =
throw new java.lang.UnsupportedOperationException("tail of empty list")
}
This is how the standard library List handles the tail of an empty list. Another, perhaps gentler, option would be to return this so that the tail of an empty MyList is just the empty MyList.
Leaving class Cons and object MyList unchanged, we get the expected results.
MyList('s','h','o','w').tail //res0: MyList[Char] = Cons(h,Cons(o,Cons(w,MyNil)))
MyList(9).tail.tail //java.lang.Unsupported...
.sum
This is a bit trickier. We want each .sum invocation to compile only if the elements are of a sum-able type, such as Int. The Scala way to achieve this to require that the call site provide implicit "evidence" that the element type is acceptable.
sealed trait MyList[+A] {
def sum(implicit ev : A =:= Int) : Int //can sum only if A is Int
}
Alas, this won't compile because MyList is covariant on A, but being the type of a passed parameter puts A in a contra-variant position.
Error: covariant type A occurs in invariant position in type A =:= Int of value ev
Fortunately there's a fix for that: use a different type parameter, related to A but not restricted to its covariant relationship.
sealed trait MyList[+A] {
def sum[B >: A](implicit ev : B =:= Int) : Int = 0 //default behavior
}
case object MyNil extends MyList[Nothing] { ... //unchanged
case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A] {
override def sum[B >: A](implicit ev :B =:= Int) : Int = head + tail.sum[B]
}
object MyList { ... //unchanged
MyList(23,31,12).sum //res0: Int = 66
MyList("as","is").sum //won't compile
Numeric[A]
Well that works for Int, but it would be a pain to have to do the same for every sum-able type. Fortunately the standard library offers the Numeric typeclass which provides some basic values (zero and one) and operations (plus(), minus(), times(), etc.) for all the numeric types under its umbrella (Short, Long, Float, etc.).
So, putting it all together:
sealed trait MyList[+A] {
val tail: MyList[A]
def sum[B >: A](implicit ev : Numeric[B]): B = ev.zero
}
case object MyNil extends MyList[Nothing] {
val tail: MyList[Nothing] = this
}
case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A] {
override def sum[B >: A](implicit ev : Numeric[B]): B = ev.plus(head, tail.sum[B])
}
object MyList {
def apply[A](as: A*): MyList[A] =
if (as.isEmpty) MyNil else Cons(as.head, apply(as.tail: _*))
}
I would like to apply a function to all objects in a list, where all objects in the list inherit from a common class. In this function, I would like to use an implicit class to ensure that the correct operation is applied based on the object's type.
For example, I want to ensure that all Employee objects in a list are converted using the employeeConverter below. Calling convert with the Employee directly works just fine, but applying convert to a list of Employee objects is a compiler error.
import scala.reflect.ClassTag
object Example {
abstract class Person { def age: Int }
case class Employee(age: Int) extends Person
class Converter[T] { def convert(t: T) = (t,t) }
def convert[T <: Person:ClassTag](p: T)(implicit converter: Converter[T]) =
converter.convert(p)
def main(args: Array[String]): Unit = {
implicit val employeeConverter = new Converter[Employee]()
println(convert(Employee(1)))
//println(List(Employee(2)) map convert) // COMPILER ERROR
}
}
The above code correctly prints the following:
$ scalac Example.scala && scala Example
(Employee(1),Employee(1))
However, if I uncomment the line indicated with COMPILER ERROR, I get this compiler error:
Example.scala:20: error: could not find implicit value for parameter converter: Example.Converter[T]
println(l map convert)
^
Is this a problem that can be resolved using ClassTag? How can I modify this example to apply convert to a list?
The compiler needs a little bit of hand-holding in this case. This works:
println(List(Employee(2)) map { e => convert(e) })
I am using some functions that return Options, but I would like to replace them by PartialFunctions in order to use the collect function in Scala. In detail I am trying to call collect (and collectFirst) on a list of objects that contain a partial function in the following way:
class A(val bs : List[B]) {
def getAll(i : Int): List[Int] = bs.map(_.foo(i))
}
class B(val y : Int) {
val foo : PartialFunction[Int, Int] = {
case x if x > y => y
}
}
The above code compiles and does what I want to if the function foo is defined for all values in bs.
val a = new A(List(new B(1), new B(2), new B(3)));
println(a.getAll(5))
prints "List(1, 2, 3)", but of course I run into an error if foo is not defined for a value. Hence, I want to replace it by "collect"
def getAll(i : Int): List[Int] = bs.collect(_.foo(i))
In my understanding collect should work pretty much the same as map, yet still the above code does not compile (I get a type mismatch because foo cannot be resolved). What is the best way in this case?
collect expects to receive a partial function (not a function or lambda), that will take an element of your collection as an input: PartialFunction[B, Int]. But here you want just to call your PartialFunction[Int, Int] inside another function, so you don't even passing a PartialFunction into collect (you're passing function (_.foo(i)): B => Int).
Solution without collect (but still with PartialFunction as a member of B):
def getAll(i : Int): List[Int] = bs.flatMap(_.foo.lift(i))
lift rises your function from PartialFunction[Int, Int] to Int => Option[Int] here.
If you really really want to collect:
import Function._
def getAll(i : Int): List[Int] = bs.collect(unlift(_.foo.lift(i)))
unlift reduces B => Option[Int] into PartialFunction[B, Int]
The ugliest version is collect{ case x if x.foo.isDefinedAt(i) => x.foo(i) }
Better solution is to move your partial function outside of B:
case class A(bs : List[B]) {
def getAll(x : Int): List[Int] = bs.collect {
case B(y) if x > y => y
}
}
case class B(val y : Int)
I was playing with method redefinition, and I found this silly example :
class a =
object
method get (x : a) = x
end
class b =
object
inherit a
method get (x : b) = x
end
I'm clearly specifying that I want the get method from the b class to take a b and return a b, but the method signature is a -> a. And if I do
(new b)#get(new a)
he's very happy, when he really shouldn't. After that I added something silly :
class a =
object
method get (x : a) = x
end
class b =
object
inherit a
method get (x : b) = x#foo(x)
method foo (x : a) = x
end
And I get Error: This expression has type b
It has no method foo
What on earth is happening ?
Taking the first example first: OCaml has structural typing of objects, not nominal typing. In other words, the type of an object is determined entirely by its methods (and their types). So the classes a and b are in fact the same type.
$ ocaml
OCaml version 4.00.0
# class a = object method get (x: a) = x end;;
class a : object method get : a -> a end
# class b = object inherit a method get (x: b) = x end;;
class b : object method get : a -> a end
# let a0 = new a;;
val a0 : a = <obj>
# let b0 = new b;;
val b0 : b = <obj>
# (a0: b);;
- : b = <obj>
# (a0: a);;
- : a = <obj>
# (b0: a);;
- : a = <obj>
# (b0: b);;
- : b = <obj>
#
(What I'm trying to show here is that both a0 and b0 are of type a and of type b.)
In the second example, I'd say you're trying to give a new type to the get method. When you override a method in OCaml, the parameter and return types need to be the same as in the parent class.
The error message seems unfortunate. My guess is that the compiler is believing you that type b is another name for type a.
One of OCaml's strengths is that it will infer types. If you leave off the : b for the parameter of get in class b, you get the following error instead:
This expression has type a. It has no method foo
This is a little more helpful, in that it shows (I think) that you're required to have type a for the parameter.
Side comment (forgive me): if you come to the OO part of OCaml from a mainstream OO language, it might strike you as strange. But if you learn the FP part of OCaml first, you might wonder why all the mainstream OO languages get so many things wrong :-). (Granted, everything is tradeoffs and there is no one right way to structure computation. But OCaml's OO subsystem is doing something pretty impressive.)
Say I'm defining a simple 2D point class in Scala, and I want to be able to construct it with various types:
class Point(x:Float, y:Float) {
this(x:Double, y:Double) = this(x.toFloat, y.toFloat)
this(x:Int, y:Int) = this(x.toFloat, y.toFloat)
// etc...
}
I want to boil this down using a template, such as:
class Point(x:Float, y:Float) {
this[T](x:T, y:T) = this(x.toFloat, y.toFloat)
}
I know this won't work anyway, since T could be a type for which toFloat isn't defined, but the compiler error I get is:
no type parameters allowed here
Is this just unsupported in Scala? If so, why, and is there any simple way to get around this?
Scala's class constructors (unlike Java's) can't take type parameters, only the class itself can. As to why Scala made this design choice, I assume the main reason is simplicity.
If you want a secondary "builder" method that is generic, the natural thing to do is define it on the companion object. For example,
object Point {
def build[T : Numeric](x: T, y: T) = {
val n = implicitly[Numeric[T]]
new Point(n.toFloat(x), n.toFloat(y))
}
}
class Point(val x:Float, val y:Float)
val p = Point.build(1, 2) // Companion object's builder
p.x + p.y
Here I've used the Numeric typeclass to get a generic toFloat method.
I played with this for awhile, getting as "close" as...
class Point(x:Float, y:Float) {
def this[T <: Any { def toFloat: Float }](x:T, y:T) = this(x.toFloat, y.toFloat)
}
...which results in "error: no type parameters allowed here" (just as per the post) and then I realized...
If the initializer could take type parameters it would be ambiguous with the class parameters, if any. Not that this couldn't be worked about in the language specification... but it is a more complex case at the very least. There might also be Java interoperability issues.
Imagine:
class Foo[T](x: T) {
def this[X](z: X) = ...
}
new Foo[Int](42) // T is Int? X is ...? Or ...?
Personally I wish Scala followed an Eiffel-like pattern (only named constructors or "factory methods"), but alas, that would not be Scala.
Happy coding.