Like many of you, my first introduction to Scala was reading books and articles and endless conversations with other, more experienced, Scala developers. I learned a few things in these conversations, but to really get to grips with Scala I had to trudge through the Internet and many examples that weren’t exactly what I needed. Don’t get me wrong, the information I found was useful, but the articles I read, like the developers I spoke to, took a top-down approach, utilising one library or the other to exemplify their meaning.

“Smokey, this is not Nam. This is bowling. There are rules .” – Walter Sobchak

What I needed was a bottom-up introduction to the language and these were few and far between. For novice Scala programmers who haven’t been exposed to functional programming before, this can be a real bummer.

“It’s a bummer dude! ” – The Dude

What I want to do in this, and subsequent posts, is start at the beginning and quickly introduce topics from a Scala perspective.

Introduction

One very powerful feature of Scala is its ability to generalise across classes and functions that take type parameters. This concept is known as ‘Higher Kinded Types’. In this post I’m going to demonstrate how to create your own Higher Kinded Type, a Functor. Before we discuss Higher Kinded Types we should talk about type constructors, but before that, let’s talk about types. How about that for starting at the bottom.

Isn’t it enough to see that a garden is beautiful without having to believe that there are fairies at the bottom of it too?– Douglas Adams

Scala Types

Scala includes the basic atomic types, Int, Long, String, Double etc. which are easy to declare and use.

val name = "Joe"
val age = 24
val isSingle = true
val isOver18 = age > 18

Two points about the code above are:

  1. These basic types in Scala (Int, Long, String, Double etc.) are in fact classes.
  2. Scala is strictly typed. The compiler is merely inferring the types of the objects by the values being assigned to them.

So a stricter, and equally valid example would be:

val name: String = "Joe"
val age: Int = 24
val isSingle: Boolean = true
val isOver18: Boolean = age > 18

Type Constructors

Now that we’ve declared some instances of atomic types, let’s try and declare a List.

val animals: List = List("Dog", "Cat", "Spider")

The code above will not compile. Try it! This is because the class List is not like the classes Int, Long, String, Double etc. The List Class takes a type parameter. If you were to look at the definition of List in the Scala language you’d see something like:

abstract class List[A]

List[A] is not a type, it’s a type constructor, where ‘A’ is the type we want to store in our list. Unlike Int, Double etc. List is a Higher Kinded Type. So, to create a list of String we actually need to use something like the following:

val animals: List[String] = List("Dog", "Cat", "Spider") 

Or we could leave the type out and the compiler will infer it, as follows:

val animals = List("Dog", "Cat", "Spider")

One point to note here is that I am not calling the List constructor directly (List is an abstract class). I’m calling a factory method provided by the List companion object.

There are many Higher Kinded Types provided by Scala, such as Map[A, B] and Option[T]. You can think of type constructors like Map[A, B] and Option[T] as something like functions taking type parameters. The concrete underlying type only exists when the type parameters are provided. 

But how would we create our own Higher Kinded Type? Before we get into that, let’s talk about notation.

Type Notation

Quite often you’ll see the following notation when reading about Higher Kinded Types: * -> *  or * -> * -> *

* -> * means given one type in a type constructor create another type. So, given List[Int] create a List of Int, or given Option[String] create an Option of String. * -> * -> * means given two types in a type constructor create a third type. So, given Map[Int, String] create a Map of Int to String.

Our First Higher Kinded Type

For our first Higher Kinded Type lets implement a simple Box class. The Box class is just a container for a value v of type T.

// kind: * -> *
class Box[T](val v: T)

A couple of things to note here:

  1. Box has a type constructor Box[T]. This says that when we declare an instance of Box, the programmer creating that Box will tell you what type to use in place of T, but for the moment we don’t care what it is.
  2. The constructor of Box takes one parameter of type T. Again, we don’t care what “type” T is at this point.

Try using this class in the Scala REPL.

scala> val x = new Box(1)
x: Box[Int] = Box@5e85c21b
scala> x.v
res0: Int = 1

Some points to note:

  1. We have a class called Box that can hold a value of any type.
  2. As we used val v: T we can read the value stored in the Box but we cannot change it.
  3. If we had used the keyword var instead of val we would be able to read and change the value of v, stored in the Box. Don’t use val! There are occasions where val is useful, but not many. Making v ‘changeable’ defeats the purpose of having a class and leads to bad side-effects.

You might be asking yourself why we want to store values in Box? Why not just use an Int, or String or whatever type we need? There are several places in Scala where Higher Kinded Types are used, such as collection classes like List, Set etc. where several instances of the same type need to be collected together. Also there are the ever-useful Option and Either classes. These are basically just boxes for one or more instances of other classes. As we’ll see in a later post Monads are also a kind of Box for values and they have some really nifty uses in Scala.

Ok, so you’ve got your value stored in the Box. How, do I perform operations on that value if I can’t change it? The answer is, you don’t. Instead you add functions to your Box class that will all you to perform perform whatever operation you want and return a new instance of your Box class containing the resulting value. The original Box class and its internal value remain unchanged. Programmers familiar with functional programming languages like Haskell will be familiar with the concept of Immutable Values. Those that come from a Python, Java or C/C++ background though tend to find it a bit alien.

“Now that’s what *I* call a close encounter.” – Capt. Steven Hiller

Functions with Type Parameters

I said at the beginning of this post that a very powerful feature of Scala is its ability to generalise across classes and functions that take type parameters. We’ve seen how type parameters can be used with classes in the Box class example above. Now, what about functions? Let’s add a function which takes a type parameter to our Box class.

class Box[T](val v: T) {
  def map[R](f: T => R): Box[R] = new Box(f(v))
}

Some points to note here:

  1. We’re defining a function called ‘map’ inside our Box class which takes a type parameter, R.
  2. The ‘map’ function takes a parameter ‘f’.
  3. The type of the parameter ‘f’ is ‘T => R’ which means that f is a function which takes something of type T and returns something of type R.
  4. The ‘map’ function returns a Box[R] i.e. a Box instance containing a value v of type R.

The casual observer might see what’s going on here. The ‘map’ function is designed to be called on an instance of Box[T] and return an instance of Box[R] using the provided function ‘f’ to convert the internal value ‘v’. Technically, the Box class is now a Functor. You can think of a Functor as any class with an appropriate map function. Let’s give it a go!

scala> val x = new Box(1)
x: Box[Int] = Box@62b6c045

scala> def convert(i: Int): Double = i.toDouble
convert: (i: Int)Double

scala> val y = x.map[Double](convert)
y: Box[Double] = Box@1d622556

scala> y.v
res1: Double = 1.0

So what have we done here?

  1. We created an instance of Box[Int] (i.e T = Int) ‘x’ containing the value 1.
  2. We then defined a function ‘convert’ which takes an Int and returns a Double. That is, it converts an Int to a Double.
  3. We then create a new instance of Box[Double] ‘y’ by calling the ‘map’ function on ‘x’, providing the ‘convert’ function as the parameter ‘f’ of map.

So we never change the value stored in the Box x. We just create a new Box y, containing a the new converted value. One thing to note, is that nothing says that the type parameter R to the ‘map’ function has to be different to the type T. So say we wanted to multiply the the value in the box by 10.

scala> def multByTen(i: Int): Int = i*10
multByTen: (i: Int)Int

scala> val x = new Box(2)
x: Box[Int] = Box@6fb22ae3

scala> val y = x.map(multByTen)
y: Box[Int] = Box@1c92a549

scala> y.v
res0: Int = 20

What’s your point, Vanessa?– Austin Powers, International Man of Mystery

The point of doing all this is there are no side-effects. Side-effects are intentional or unintentional modifications to the values stored in a class. Intentional or not, side-effects are responsible for an awful lot of bugs in code. All developers have seen it and/or done it. You create an object and pass it around your program like a football. After a while, there’s no telling what’s stored in it any more. Taking advantage of Higher Kinded Types to provide immutable values in your code from the start, alleviates this and get’s rid of a lot of pesky bugs.

The only good bug, is a dead bug.– Unnamed Citizen, Starship Troopers

Functors

I said above that we don’t change the value in our Box class, instead you add functions to your Box class that will allow you to perform whatever operation you want and return a new instance of your Box class containing the resulting value. By adding the ‘map’ function to our Box class we changed it into something called a Functor. A Functor is just something that can be mapped over. In this case a Functor is a class that provides developers with the ability to pass functions to be applied to its internal values.

This is quite a powerful concept. By adding the ‘map’ function to Box we were able to apply several different functions to the value contained in a Box. In fact you can apply any function with the signature f: T => R.

One quick point here is that you will often see the map function in Functors names ‘fmap’. I simply called it map here, because I think it’s a confusing convention for novice Scala programmers as in my experience they confuse it with ‘flatMap’, another function we’ll cover later.

There is nothing to prevent you from adding other functions to the Box class if you like. In later posts, I’ll cover Applicative and Monad which, like Functor, have specific functions in their contract.

Conclusion

So that’s the first post in what I hope will be a series of posts on Scala. I’ve tried to keep it light and not intimidating. I hope you find it useful. Feel free to leave a comment or suggestion on current or future posts.

Leave a Reply

Your email address will not be published. Required fields are marked *