8 - Implicits

Another powerful Scala feature today.
Implicits do a lot of black magic behind the scenes.
Today we learn how to control the magic.

You can find today's contents here.

Practice

We'll extend an existing well-known type with a few methods, use implicits to sort collections of our own datatypes and finally try to improve equality operations in Scala.

  1. Define the class RichInteger that takes an Int parameter:
    class RichInteger(val x: Int)
    • Define the sqrt method on it. It should do the square root of x. Use an implicit conversion from Int to RichInteger to enable this operation on integers.
    • Convert the previous code to an implicit class. How does this help you?
    • Add an isPrime method to RichInteger. It should check whether x is prime or not.
    • Add the gcd method to RichInteger. It should receive another y parameter of type Int and compute the greatest common divisor of the two.
    • Test the new magic methods you just created.
  2. Define a data type for semantic versioning.
    • Use a case class and name it Version. It should have 3 fields, all integers: major, minor and patch.
    • Define the < operator on the new data type.
    • Define a value for a Version ordering. Its type should be Ordering[Version]
      val order: Ordering[Version] = ???
    • Create a list of versions. Try calling the sorted method on it. Does it work?
    • Promote the ordering defined earlier to an implicit value. What's the recommended place to put the implicit?
  3. Define the Eq typeclass to offer support for equality testing.
    trait Eq[A] {
      def ===(l: A, r: A): Boolean
      def =!=(l: A, r: A): Boolean
    }
    • The two operations should check for equality. Tip - you can use the existing == and != in the actual implementation.
    • Typeclasses usually define additional Ops traits in order to provide an operator like notation to the implementing types. Use the following companion object for Eq in order to enable === and =!= on any type class instance.
      object Eq {
       
        def apply[A](implicit instance: Eq[A]): Eq[A] = instance
       
        trait Ops[A] {
          def typeClassInstance: Eq[A]
          def self: A
          def ===(other: A) = typeClassInstance.===(self, other)
          def =!=(other: A) = typeClassInstance.=!=(self, other)
        }
       
        trait ToEqOps {
          implicit def toEqOps[A](target: A)(implicit instance: Eq[A]) = new Ops[A] {
            override val typeClassInstance = instance
            override val self              = target
          }
        }
       
        object ops extends ToEqOps
      }
    • Define an instance for Ints. Use the Eq object to define it. Compare two integers using the === and =!= methods. Does it work for other types as well (e.g. Strings)?
    • Define an universal instance that compares generic types. What's the difference between == vs. === and != vs =!=?
sesiuni/scala/lab8.txt · Last modified: 2016/07/06 18:55 by dciocirlan