Hacker News new | past | comments | ask | show | jobs | submit login

> It is close but typeclasses are not interfaces either.

Why not? They seem to be exactly interfaces to me.

> They could have used a fashionable and somewhat overloaded term like "interface" or "protocol" or "trait" or "flavor", or they could have used an exact, albeit somewhat bland, name coming from mathematics. They did the latter.

The terminology "class" in Haskell certainly has nothing to do with its mathematical meaning of "collection of sets defined by a predicate".




> They seem to be exactly interfaces to me.

If we are talking about interface like in Java, for instance, then the important difference is that in Java, class specifies what interfaces it has, while in Haskell, any code can specify how a type is a member of a type class (actually there can be multiple different ways how the same type can be member of same type class). Another difference was, until recently, that the type classes can define "default" implementations of functions. And there are other differences related to "deriving" statement.

> The terminology "class" in Haskell certainly has nothing to do with its mathematical meaning of "collection of sets defined by a predicate"

I don't want to get into philosophy of math, I am not strong in it, but "class" in mathematics is certainly not limited to collections of sets. For example, there is a class of all classes, but not set of all sets.

The predicate that defines the type class in Haskell is defined by the collection of all the relevant "instance" statements.

Actually, now that I think about it, as I note above, there are multiple ways that one type can be made a member of one type class, so it's certainly not a membership in the set-theoretic sense.


In popular OOP languages (C#, Java, etc), interfaces give you a way to state that some existentially quantified type implements some set of common methods. Here's what I mean by "existentially quantified":

Let's say you have some function called GetStringableThing() that returns returns, as its name suggests, some object that implements the Stringable interface -- where Stringable is just an interface that consists of a ToString() method. As the caller of GetStringableThing(), you have no say in precisely which type of Stringable thing you'll get -- that's up to the callee. So there exists some type that implements Stringable, which GetStringableThing() would be more than happy to give you. This is in contrast with universal quantification; if the return type of GetStringableThing() was universally quantified, that would mean that you as the caller get to dictate the return type (so long as it implements Stringable), and its the callee's responsibility to be able to return any type the caller demands.

Typeclasses (as in Haskell) have the benefit of allowing you, as the caller of a function, to dictate the return type of that function -- in fact, universal quantification is the default, and you have to go out of your way (e.g. with GADTs) to represent existential types.

This now leads us to a common pitfall for people coming to Haskell from OOP languages, where their intuition that "typeclass == interface" fails them:

    -- The intent here is the same as earlier with "Stringable",
    -- though in Haskell that would be "Show".
    --
    -- A beginner's intuition:
    -- "Surely this reads 'somethingShowable is some type implementing the Show interface', right?"
    somethingShowable :: (Show a) => a
    somethingShowable = (42 :: Int)
A beginner will often think that the above should compile ("42 is an Int, and Int has a Show instance, so this should work, right? I just need to provide something that has a Show instance, right?"). Unfortunately, it doesn't compile. The above code is making the promise that it can provide a value of any type that the caller demands (so long as it implements Show), but here we're only providing an integer specifically.

This bit of OOP pseudo-code captures what they we're going for:

    func getStringableThing() Stringable {
      return 42
    }
A natural implication of this universal vs existential quantification distinction is this:

Typeclass instances are resolved at compile time (except where you've gone out of your way to existentially type something), and can be specialized and inlined accordingly. Interfaces (a la OOP) are implemented via some sort of vtable mechanism, where dispatch is deferred until runtime (though a JIT can potentially do some inlining).

Interfaces (a la OOP) and typeclasses are implemented (and behave) quite differently, so I would argue that if Haskell were to replace its use of "class" (and "typeclass") with "interface" it would only confuse things further (as evidenced by the constant stream of beginners asking for help where they've made this exact conflation; for the record, I was one of these beginners 3 years ago).

The "they shouldn't call these (type)classes!" complaint is quite tiring: no one ever has a reasonable alternative to offer. At least (type)class makes some sense when you realize that "class" is meant in the mathematical sense of the word "class".




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: