Playing with types in Haskell (Part 2)

TypeClasses

And then you have typeclasses, which is basically a class (absolutely nothing to do with OOP classes in C/C++ or Java or similar languages) to which types may belong, and the typeclass defines certain behaviors that types belonging to it will exhibit.

For instance, in the standard library, Eq is a typeclass, and the behavior that Eq defines is that two values can be “equated”, whatever the meaning of equating two values of a type may mean.

Int is a type. Since Int belongs to Eq, it means that all values of Int (e.g. 100, 423, -200, etc) have a meaning when they are equated. This meaning is familiar to all of us, 100 == 100 is true, but 100 == 423 is not.

The manner in which typeclasses define behaviors, is through a set of one or more functions that the typeclass defines, that all members of the typeclass must implement.

For instance, in our venerable Eq typeclass, the operators (which are functions) == and /= are defined. Hence all members of the Eq typeclass must be able to be ==‘ed or /=‘ed.

And now let’s make a typeclass for our strings:

class ReturnSame a where
  returnSame :: a -> a

This is a rather useless typeclass, and the behavior is that any member of which must implement returnSame. As the name suggests, we are going to return exactly what we get, and just that. Note that “a” here is similar to the above, it’s a type variable. In this case, it represents the instance that we are going to create, and how that instance behaves/is used. To make that clearer, let’s use an example.

Let’s make our StringString and StringAnything instances of the typeclass:

instance BecomesFalse StringString where
  turnToFalse x = x

instance BecomesFalse (StringAnything a) where
  turnToFalse x = x

Let’s play with it:

turnToFalse (StringString "John" "Peter")       ==> StringString "John" "Peter"
:t turnToFalse (StringString "John" "Peter")    ==> StringString

turnToFalse (StringAnything "John" ["Peter"])       ==> StringAnything "John" ["Peter"]
:t turnToFalse (StringAnything "John" ["Peter"])    ==> StringAnything [[Char]]

There we go, we have our own type, and our own typeclass.

Understanding the Haskell type system goes a long way, and this is just touching on the tip of the iceberg. Hence, this is not meant to be an utterly complete and formal introduction to typeclasses, or types. For that, there are great resources out there. This is one of them. There are also missing bits, such as type subclasses (type constraints), and so on.

Once again, remember:

  • Value constructors are functions, and they take parameters, and produce a value

  • You cannot write StringAnything Int, in code, that’s a type constructor. You can only write StringAnything 4, that’s a value constructor.

  • The value has a type, and is defined by the type constructor

  • Type constructors that have no variables are concrete types by themselves (e.g. StringString)

  • Type constructors that have variables are polymorphic types, and are not types by themselves (e.g. StringAnything)

  • For these, you have to give the variable a value (a concrete type), and then the type constructor creates a concrete type (e.g. StringAnything Int, StringAnything [Char])