In this blog I'll post on everything thats going on with my games, I'm developing. There may be some more things, but thats the main topic.
A project I've worked on is for example WorldOfCube, download here:
Download.

22nd May 2013

Post with 2 notes

Scala and extending Numeric[T]

Starting simple

I bet you always wondered how to write a purely generic Vec2 class (a class holding a numeric x, and a numeric y, which allows mathematical operations with that so-called vector). If not, you probably wonder now! :)

In scala you usually do it like that:

class Vec2[T](val x: T, val y: T)

So far, so good. What you can do now, is allocate a Vec2 with any type, and reference it’s members which have the type you want:

val vec = new Vec2[Float](1f, 2f)
println(vec.x) // prints "1.0"

val vec2 = new Vec2[Double](1.5, 1.2)
println(vec2.x) // prints "1.5"

Still, so far, so good.

Let’s continue, We’ll add simple operations to our vector class, like +/-/* and /:

import scala.math.Fractional

class Vec2[T](val x: T, val y: T)(implicit num: Fractional[T]) {
  import num.mkNumericOps

  def +(that: Vec2[T]) = new Vec2(this.x + that.x, this.y + that.y)
  def -(that: Vec2[T]) = new Vec2(this.x - that.y, this.y - that.y)
  def *(that: Vec2[T]) = new Vec2(this.x * that.y, this.y * that.y)
  def /(that: Vec2[T]) = new Vec2(this.x / that.y, this.y / that.y)

  override def toString = s"Vec2($x, $y)"
}

To do something arithmetically with a Generic parameter we need scala’s Numeric. It allows us to add, subtract, multiplicate, divide numbers and much more. We also have to import num.mkNumericOps, since that allows us to use the infix operators on the Generic type T. Else we would always need to write something like num.plus(this.x, that.x)".

Now, we can test that code and it works:

val vec1 = new Vec2(10f, 4f)
val vec2 = new Vec2(5f, 3f)
val result = vec1 + vec2
println(result) // prints "Vec2(15.0, 7.0)"

So now we want to add a method for getting the length of a vector:

def squaredLength = x * x + y * y
def length = math.sqrt(squaredLength)

So here is the problem, the compiler won’t let us do that:

<console>:23: error: type mismatch;
 found   : T
 required: Double
        def length = math.sqrt(squaredLength)
                               ^

The problem is, that math.sqrt takes a Double as argument, it’s impossible to call it with the Generic type parameter T.

What can we do about this? Numeric doesn’t have something like num.sqrt(). But Numeric was implemented in scala, too. It’s not a compiler feature! It’s possible to write your own Numeric - or even cooler - extend your Numeric to include methods you like!

How Numeric works: Write your own

Let’s do this. What do we need for this? First, let’s understand how Numeric works. Let’s write our own simple Numeric. It should only be able to add two numbers…

trait MyNumeric[T] {
  def plus(x: T, y: T): T
}

The Idea of the Numeric interface is to make scala choose the right numeric for the right type. So We create a numeric for Float, Double and Int, and it’ll choose the right Object, and if T is none of those, it’ll cry like a baby.

That can be done with implicit objects in scala. They can be used for a so called type class pattern. If you know Haskell, you should know type classes very well.

trait MyNumeric[T] {
  def plus(x: T, y: T): T
}
object MyNumeric {
  implicit object IntIsMyNumeric extends MyNumeric[Int] {
    def plus(x: Int, y: Int) = x + y
  }
  implicit object FloatIsMyNumeric extends MyNumeric[Float] {
    def plus(x: Float, y: Float) = x + y
  }
  implicit object StringIsMyNumeric extends MyNumeric[String] {
    def plus(x: String, y: String) = s"($x + $y)"
  }
}

So now we’ve defined Classes which help to do adding with Int's, Float's and String's. I added Strings just for fun :)

So let’s define a sum method, which is generic (over Int, Float, and Strings). Also, the Strings don’t add like you may be used to with "abc" + "def" = "abcdef", but it represents a mathematical operation, so "1" + "2" is supposed to be "(1 + 2)" and ("1" + "2") + "3" = "((1 + 2) + 3)" and so on.

def sum[T](elements: T*)(implicit num: MyNumeric[T]) = {
    var acc = elements(0)
    for (i <- 1 until elements.size) acc = num.plus(acc, elements(i))
    acc
}

That code is written in a very, very, imperative style, but that makes it easy to understand. And since we’re missing some necessary methods to make it purely functional, we have no choice.

We can test that code and it works magically for our types that we have type classes for:

println(sum(1f, 2f, 3f, 4.1f)) // prints "10.1"
println(sum(1, 2, 3, 4, 5)) // prints "15"
println(sum("1", "2", "3", "4", "abc")) // prints "((((1 + 2) + 3) + 4) + abc)"

But what should the compiler do when he searches for the type class of type Char, for example? We haven’t defined that one, so it can’t work, right?

println(sum('c', 'd'))
<console>:16: error: could not find implicit value for parameter num: MyNumeric[Char]
              println(sum('c', 'd'))
                         ^

Yep, right :)

Extending Numeric

So now we go back to our first issue: We couldn’t call sqrt generically. But technically it’s possible! You’ve probably done it often. In java it’s called casting, in scala it’s not exactly casting, but kind of:

println(math.sqrt(5.toDouble).toInt) // prints "2"
println(math.sqrt(5f.toDouble).toFloat) // prints "2.236068"
println(math.sqrt(5.0)) // prints "2.23606797749979"

So yeah, it’s definitely possible. We can do the same in our type-classes like that:

trait MyNumeric[T] {
  def plus(x: T, y: T): T
  def sqrt(x: T): T
}
object MyNumeric {
  implicit object IntIsMyNumeric extends MyNumeric[Int] {
    def plus(x: Int, y: Int) = x + y
    def sqrt(x: Int) = math.sqrt(x).toInt
  }
  implicit object FloatIsMyNumeric extends MyNumeric[Float] {
    def plus(x: Float, y: Float) = x + y
    def sqrt(x: Float) = math.sqrt(x).toFloat
  }
  implicit object StringIsMyNumeric extends MyNumeric[String] {
    def plus(x: String, y: String) = s"($x + $y)"
    def sqrt(x: String) = s"sqrt($x)"
  }
}

And sqrt now works:

def sqrtMagic[T](x: T)(implicit num: MyNumeric[T]) = num.sqrt(x)
sqrtMagic(10f) // prints "3.1622777"
sqrtMagic("a") // prints "sqrt(a)"

Works perfectly! But we haven’t got +, -, * and stuff… That’s still a problem… :/ We also don’t want to implement them ourself, but simply extend the standard scala’s numerics now. This can be done simply with extending the classes. The idea is to have a subclass of Numeric[T], which requests having the sqrt() method. And the typeclasses of our own Numeric then extend the original Numeric's typeclasses and extend them by the sqrt() method. Also, we move everything from Numeric to Fractional, since we only want to allow calling .sqrt() on Fractional numerics, that is: Float, Double, BigDecimal and similar. We’ll only define the implicit classes for Float and Double now.

Let’s do this:

import scala.math.Fractional
import scala.math.Numeric._

trait SqrtFractional[T] extends Fractional[T] {
  def sqrt(x: T): T
}
object SqrtFractional {
  trait FloatIsSqrtFractional extends SqrtFractional[Float] {
    def sqrt(x: Float) = math.sqrt(x).toFloat
  }
  implicit object FloatIsSqrtFractional 
    extends FloatIsSqrtFractional 
    with FloatIsFractional 
    with Ordering.FloatOrdering

  trait DoubleIsSqrtFractional extends SqrtFractional[Double] {
    def sqrt(x: Double) = math.sqrt(x)
  }
  implicit object DoubleIsSqrtFractional 
    extends DoubleIsSqrtFractional 
    with DoubleIsFractional 
    with Ordering.DoubleOrdering
}

Yep. That’s it.

We can now use that code to complete our Vec2 class:

class Vec2[T](val x: T, val y: T)(implicit num: SqrtFractional[T]) {
  import num.mkNumericOps

  def +(that: Vec2[T]) = new Vec2(this.x + that.x, this.y + that.y)
  def -(that: Vec2[T]) = new Vec2(this.x - that.y, this.y - that.y)
  def *(that: Vec2[T]) = new Vec2(this.x * that.y, this.y * that.y)
  def /(that: Vec2[T]) = new Vec2(this.x / that.y, this.y / that.y)
  
  def squaredLength = x * x + y * y
  def length = num.sqrt(squaredLength)

  override def toString = s"Vec2($x, $y)"
}

println(new Vec2(10f, 10f).length) // prints "14.142136"

I hope you liked this tutorial :)

Some Extra stuff

If you ever wondered how to implement the infix operators, you’ll keep wondering, since I won’t explain it :)

But if you want to implement your own infix or suffix stuff, you can do it by extending Numeric#Ops. I’ll post an example here on how the SqrtNumeric class would look like to be able to do something like 36f.sqrt:

import scala.math.Fractional
import scala.math.Numeric._

trait SqrtFractional[T] extends Fractional[T] {
  def sqrt(x: T): T

  class SqrtNumericOps(left: T) extends Ops(left) {
    def sqrt = SqrtFractional.this.sqrt(left)
  }
  implicit def mkSqrtNumericOps(left: T) = new SqrtNumericOps(left)
}
object SqrtFractional {
  trait FloatIsSqrtFractional extends SqrtFractional[Float] {
    def sqrt(x: Float) = math.sqrt(x).toFloat
  }
  implicit object FloatIsSqrtFractional 
    extends FloatIsSqrtFractional 
    with FloatIsFractional 
    with Ordering.FloatOrdering

  trait DoubleIsSqrtFractional extends SqrtFractional[Double] {
    def sqrt(x: Double) = math.sqrt(x)
  }
  implicit object DoubleIsSqrtFractional 
    extends DoubleIsSqrtFractional 
    with DoubleIsFractional 
    with Ordering.DoubleOrdering
}

def lolwut[T](n: T)(implicit num: SqrtFractional[T]) {
  import num.mkSqrtNumericOps
  println(n.sqrt)
}
lolwut(36f) // prints "6.0"

Self-advertisement

Just so you don’t have to create it all yourself, I’ve implemented a MathFractional which allows dividing and supports all mathematical operations from scala.math

Also, I’ve already implemented the Vec2, (and Vec3 and Vec4)…

Tagged: scalaprogramming

  1. matheusdev posted this