
Haskell: OOP vs type classes - rbxbx
http://www.haskell.org/haskellwiki/OOP_vs_type_classes
======
ekidd
Haskell doesn't really do inheritance, unless you somehow get O'Haskell to
work on a modern machine.

In place of inheritance, you have two mechanisms: algebraic data types and
type classes. They work great for many programs, but neither of them is an
exact fit for Java-style object-oriented programming.

An algebraic data type has a name ("Bool", in the first example below), and
several constructors ("True" and "False"). Constructors can take positional
and named arguments, and they can take type parameters:

    
    
        data Bool = True | False
        data Shape =
            Square { l :: Int, t :: Int, w :: Int, h :: Int }
          | Circle Int Int Int
        data Maybe a = Just a | Nothing
    

Once you define an algebraic data type, you can't add new constructors. But
you can write functions which match against the existing constructors at
runtime, giving you a form of runtime dispatch vaguely analogous to method
lookup:

    
    
        boolToString :: Bool -> String
        boolToString True = "true"
        boolToString False = "false"
    

So algebraic data types work great if you have one abstract interface with
known set of concrete subclasses. It's a _really_ great way to think about
complex data structures with multiple types of nodes.

A type class is a little bit like a C++ template protocol: It says that a
given type implements a set of functions. But as with C++ templates, it's
effectively resolved at compile time. (Well, the implementation is quite a bit
different, but that's the rough idea.)

Now, ADTs and type classes are great. But if you find yourself using crazy
hacks to recreate an OO class hierarchy in Haskell, you're probably fighting
against the language. Either structure your program differently, or find a
different language.

~~~
dmooney1
I agree - type overloading says templates to me rather than inheritance. C++'s
tuple class lets you do some of the cool listy type things you can do in
Haskell. Using typedef tuple<double, double> FloatPoint and taking it from
there seems a lot more like the Haskell example and would be less code - one
line each for the typedefs and a one-line function to implement <<. (Not
taking anything away from Haskell, which I'm completely in love with.)

~~~
kd0amg
The typedef tuple strategy doesn't let you distinguish between types except by
the types and ordering of their fields. If you later have a constructor for
another ADT (or even the same one) which also takes two Float arguments, you
end up with multiple typedefs for tuple<double, double>.

------
gtani
Well then, the expression problem. The guys who created scala and OCaml have
opinions about this, no surprise (why not OOP and typeclasses?

[http://stackoverflow.com/questions/2807629/handling-
incremen...](http://stackoverflow.com/questions/2807629/handling-incremental-
data-modeling-changes-in-functional-programming)

(message 20) [http://groups.google.com/group/scala-
debate/browse_frm/threa...](http://groups.google.com/group/scala-
debate/browse_frm/thread/96193eacf013a103/)

<http://lambda-the-ultimate.org/node/4400>

\---------------

and "ADT" i think real world haskell is correct in reservign this for abstract
data types

[http://book.realworldhaskell.org/read/defining-types-
streaml...](http://book.realworldhaskell.org/read/defining-types-streamlining-
functions.html#deftypes.adt)

------
Chris_Newton
It is unfortunate that two completely different concepts both happen to use
the word "class" in their name. A related example is "return". People coming
from a background in, say, C++ or Java will naturally have preconceptions
about what these words mean, and will almost inevitably start by trying to
apply them in Haskell world where they don't work.

It is particularly unfortunate because, with the wisdom of hindsight, I think
classes as used in C++ and Java have been responsible for many problems over
the years. This is fundamentally because they take two very common, very
useful ideas -- modular design (separation of interface and implementation)
and compound data types (struct/record/variant/etc.) -- and try to use a
single tool to implement both at the same time.

Unfortunately, that creates all kinds of presumptions about how you write your
functions, where the emphasis is always on one object being paramount or one
data type controlling everything. For practical programming tasks, it may be
more useful to model a situation using a set of related types to hold the data
and a set of algorithms defined in terms of one _or more_ of those data types
(or more generally _collections_ of one or more such types) to manipulate that
data.

It's probably not an exaggeration to say that almost all of the most common
modelling problems with C++/Java style OOP -- the "diamond pattern" with
multiple inheritance, for example -- are due at least in part to this
unwarranted emphasis on this/self/whatever you want to call it. Much of the
rest is due to over-use of inheritance, which again has an underlying
subclass-or-subtype ambiguity in what it represents -- and which again is
perhaps emphasized by the whole one-object-to-rule-the-world philosophy even
when what OOP would call containment or aggregation is a clearer way to model
the situation.

Languages such as Haskell have never had this history of conflating modular
design with structuring data, nor the resulting false friend of emphasizing
one object/value/whatever in any given modelling context. IME, this is the big
conceptual leap that is sometimes hard for those with a strongly everything-
is-an-object background to make, though it's perhaps a little easier these
days for anyone who has used C++ templates or Java generics. Once you make the
jump, type classes and algebraic data types make a lot more sense.

------
jmartinpetersen
There's a tech talk with Simon Peyton Jones giving an introduction to type
classes in Haskell as well as discussing how it relates to OO at
[http://channel9.msdn.com/posts/MDCC-TechTalk-Classes-Jim-
but...](http://channel9.msdn.com/posts/MDCC-TechTalk-Classes-Jim-but-not-as-
we-know-them)

------
langsamer
I think it is a bit silly to try to re-create OO with type classes or vice-
versa. They are just two different modes of thinking and represent two
different ways of architecting your program. Personally, I do find OOP a hoax,
which leads to just horrendous code, but I'm sure either approach can be used
to solve the problem at hand as long as you don't try to fit the round-peg in
the square-hole.

For a case against OOP read :
<http://c2.com/cgi/wiki?ObjectOrientationIsaHoax>

------
tikhonj
I haven't had time to read through the whole article (and I already know how
type classes work :)) so I just have one little point to add.

As correctly put in the article, type classes can provide default
implementations. However, one cute thing that was missed is that type classes
can provide default implementations for _every_ function, referencing each
other:

    
    
        class  Eq a  where
        (==), (/=) :: a -> a -> Bool
        x /= y     =  not (x == y)
        x == y     =  not (x /= y)
    

This way you can implement == _or_ /=, and you get the other for free.

For me, the one new idea that made me understand why type classes are awesome
was the read function: it's polymorphic on its _return type_. This means that
you always give it a String and it returns whatever you need. This also means
that you can have polymorphic constants--maxBound is the maximum of _any_
bounded numeric type, so you can use it as an Int or a Float and it will
always have the right value.

Overall, I now think that type classes are awesome. However, I also don't
think a parallel to OOP is particularly helpful--I also learned OOP well
before functional programming and found it easier to understand type classes
by looking at how they were used than by comparing them to interfaces or
templates.

------
rbxbx
Speaking as someone who's brain has been broken by OOP, I found this article
quite illuminating and would love to see more of the hardcore haskell/ML/FP
guys weigh in.

~~~
jlouis
* The Haskell guys can use Algebraic Datatypes and type classes to achieve many of the desirable properties of OOP.

* The ML guys can use OCaml, where the O is Objective. Ocaml contains a concept of OOP built into the language already (namely structural subtyping - someone could call this typed duck-typing). The ML guys in general also have another tool for abstraction at their disposal: ML-style functors.

* Erlang programmers often use processes which are self-contained concurrently executing "objects". In Erlang a process is lightweight enough to be considered a "heavy" object in OOP so you often structure your programs around several processes messaging each other to carry out solutions to tasks.

Different languages provides different tools as a means of abstraction. It is
all just about unearthing the alternative.

------
nwmcsween
I always thought OOP vs other paradigms is simply structuring the program,
with enough time and work you could make a any program in any paradigm you
wish. Why not pluggable paradigms?

