A lot of people have difficulty understanding type-classes. This has a lot more to do with the context of type-classes than type-classes themselves; Haskell, the foremost language with type-classes, also includes more complicated concepts enabled by type-classes. People often conflate these concepts with type-classes themselves.
Type-classes in Scala are similarly conflated with more complicated concepts and have the added detriment of being a pattern rather than a language feature.
In this post I'll explain type-classes by relating them to somehting everybody understands: the List interface in Java.
public interface List<E> {
public E get(int index) = ...
public int size() = ...
public boolean isEmpty()
...
}
public class Array<E> implements List<E> {
public E get(int index) = ...
public int size() = ...
public boolean isEmpty()
...
}
The type-class version is almost identical.
class List list where
get :: list e -> Int -> e
size :: list e -> Int
isEmpty :: list e -> Bool
...
data Array e = ...
instance List Array where
get arr index =
size arr =
isEmpty arr =
...
The only difference is that the implementation of List
for Array
has been separated from the definition of Array
. That is the entire idea of type-classes and how they differ from classic interfaces. This minor difference allows library authors to create new interfaces and implement them for data types in code they don't own, even code in the standard library.
Since we're a Scala shop, here is the equivalent code in Scala:
trait List[L[_]] {
def get[E](list : L[E], index : Int) : E = ...
def size[E](list : L[E]) : Int = ...
def isEmpty[E](list : L[E]) : Boolean = ...
}
class Array[E] {
...
}
object Array {
implicit val arrayListInstance = new List[Array] {
def get[E](list : L[E], index : Int) : E = ...
def size[E](list : L[E]) : Int = ...
def isEmpty[E](list : L[E]) : Boolean = ...
}
}