Type classes vs object interfaces I don't think I understand type classes. I'd read somewhere that thinking of type classes as "interfaces" (from OO) that a type implements is wrong and misleading.
The problem is, I'm having a problem seeing them as something different and how that is wrong.

For example, if I have a type class (in Haskell syntax)

class Functor f where
  fmap :: (a -&gt; b) -&gt; f a -&gt; f b


How is that different than the interface [1] (in Java syntax)

interface Functor&lt;A&gt; {
  &lt;B&gt; Functor&lt;B&gt; fmap(Function&lt;B, A&gt; fn)
}

interface Function&lt;Return, Argument&gt; {
  Return apply(Argument arg);
}


One possible difference I can think of is that the type class implementation used at a certain invocation is not specified but rather determined from the environment -- say, examining available modules for an implementation for this type. That seems to be an implementation artifact that could be addressed in an OO language; like the compiler (or runtime) could scan for a wrapper/extender/monkey-patcher that exposes the necessary interface on the type.

What am I missing?

[1] Note the f a argument has been removed from fmap since given it's an OO language, you'd be calling this method on an object. This interface assumes the f a argument has been fixed.
In their basic form, type classes are somewhat similar to object interfaces. However, in many respects, they are much more general.


Dispatch is on types, not values. No value is required to perform it. For example, it is possible to do dispatch on the result type of function, as with Haskell's Read class:

class Read a where
  readsPrec :: Int -&gt; String -&gt; [(a, String)]
  ...


Such dispatch is clearly impossible in conventional OO.
Type classes naturally extend to multiple dispatch, simply by providing multiple parameters:

class Mul a b c where
  (*) :: a -&gt; b -&gt; c

instance Mul Int Int Int where ...
instance Mul Int Vec Vec where ...
instance Mul Vec Vec Int where ...

Instance definitions are independent from both class and type definitions, which makes them more modular. A type T from module A can be retrofitted to a class C from module M2 without modifying the definition of either, simply by providing an instance in module M3. In OO, this requires more esoteric (and less OO-ish) language features like extension methods.
Type classes are based on parametric polymorphism, not subtyping. That enables more accurate typing. Consider e.g.

pick :: Enum a =&gt; a -&gt; a -&gt; a
pick x y = if fromEnum x == 0 then y else x


vs.

pick(x : Enum, y : Enum) : Enum = if x.fromEnum() == 0 then y else x


In the former case, applying pick '\0' 'x' has type Char, whereas in the latter case, all you'd know about the result would be that it's an Enum. (This is also the reason why most OO languages these days integrate parametric polymorphism.)
Closely related is the issue of binary methods. They are completely natural with type classes:

class Ord a where
  (&lt;) :: a -&gt; a -&gt; Bool
  ...

min :: Ord a =&gt; a -&gt; a -&gt; a
min x y = if x &lt; y then x else y


With subtyping alone, the Ord interface is impossible to express. You need a more complicated, recursive form or parametric polymorphism called "F-bounded quantification" to do it accurately. Compare Java's Comparable and its use:

interface Comparable&lt;T&gt; {
  int compareTo(T y);
};

&lt;T extends Comparable&lt;T&gt;&gt; T min(T x, T y) {
  if (x.compareTo(y) &lt; 0)
    return x;
  else
    return y;
}



On the other hand, subtyping-based interfaces naturally allow the formation of heterogeneous collections, e.g. a list of type List&lt;C&gt; can contain members that have various subtypes of C (although it is not possible to recover their exact type, except by using downcasts). To do the same based on type classes, you need existential types as an additional feature.