Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a Haskell equivalent of OOP's abstract classes, using algebraic data types or polymorphism?

In Haskell, is it possible to write a function with a signature that can accept two different (although similar) data types, and operate differently depending on what type is passed in?

An example might make my question clearer. If I have a function named myFunction, and two types named MyTypeA and MyTypeB, can I define myFunction so that it can only accept data of type MyTypeA or MyTypeB as its first parameter?

type MyTypeA = (Int, Int, Char, Char) type MyTypeB = ([Int], [Char])  myFunction :: MyTypeA_or_MyTypeB -> Char myFunction constrainedToTypeA = something myFunction constrainedToTypeB = somethingElse 

In an OOP language, you could write what I'm trying to achieve like so:

public abstract class ConstrainedType { }  public class MyTypeA extends ConstrainedType {     ...various members... }  public class MyTypeB extends ConstrainedType {     ...various members... }  ...  public Char myFunction(ConstrainedType a) {     if (a TypeOf MyTypeA) {         return doStuffA();     }     else if (a TypeOf MyTypeB) {         return doStuffB();     } } 

I've been reading about algebraic data types and I think I need to define a Haskell type, but I'm not sure how to go about defining it so that it can store one type or another, and also how I use it in my own functions.

like image 554
ultrafez Avatar asked Oct 27 '10 02:10

ultrafez


People also ask

Is Haskell an OOP?

Haskell isn't an object-oriented language. All of the functionality built here from scratch already exists in a much more powerful form, using Haskell's type system.

Does Haskell have classes?

Haskell classes are roughly similar to a Java interface. Like an interface declaration, a Haskell class declaration defines a protocol for using an object rather than defining an object itself.

What is a Typeclass in Haskell?

Type Classes are a language mechanism in Haskell designed to support general overloading in a principled way. They address each of the concerns raised above. They provide concise types to describe overloaded functions, so there is no expo- nential blow-up in the number of versions of an overloaded function.

What is data Haskell?

In Haskell, you can have many constructors for your data type, separated by a vertical bar | . Each of your constructors then has its own list of data types! So different constructors of the same type can have different underlying data! We refer to a type with multiple constructors as a “sum” type.


2 Answers

Yes, you are correct, you are looking for algebraic data types. There is a great tutorial on them at Learn You a Haskell.

For the record, the concept of an abstract class from OOP actually has three different translations into Haskell, and ADTs are just one. Here is a quick overview of the techniques.

Algebraic Data Types

Algebraic data types encode the pattern of an abstract class whose subclasses are known, and where functions check which particular instance the object is a member of by down-casting.

abstract class IntBox { }  class Empty : IntBox { }  class Full : IntBox {     int inside;     Full(int inside) { this.inside = inside; } }  int Get(IntBox a) {     if (a is Empty) { return 0; }     if (a is Full)  { return ((Full)a).inside; }     error("IntBox not of expected type"); } 

Translates into:

data IntBox = Empty | Full Int  get :: IntBox -> Int get Empty = 0 get (Full x) = x 

Record of functions

This style does not allow down-casting, so the Get function above would not be expressible in this style. So here is something completely different.

abstract class Animal {      abstract string CatchPhrase();     virtual void Speak() { print(CatchPhrase()); } }  class Cat : Animal {     override string CatchPhrase() { return "Meow"; } }  class Dog : Animal {     override string CatchPhrase() { return "Woof"; }     override void Speak() { print("Rowwrlrw"); } } 

Its translation in Haskell doesn't map types into types. Animal is the only type, and Dog and Cat are squashed away into their constructor functions:

data Animal = Animal {     catchPhrase :: String,     speak       :: IO () }  protoAnimal :: Animal protoAnimal = Animal {     speak = putStrLn (catchPhrase protoAnimal) }  cat :: Animal cat = protoAnimal { catchPhrase = "Meow" }  dog :: Animal dog = protoAnimal { catchPhrase = "Woof", speak = putStrLn "Rowwrlrw" } 

There are a few different permutations of this basic concept. The invariant is that the abstract type is a record type where the methods are the fields of the record.

EDIT: There is a good discussion in the comments on some of the subtleties of this approach, including a bug in the above code.

Typeclasses

This is my least favorite encoding of OO ideas. It is comfortable to OO programmers because it uses familiar words and maps types to types. But the record of functions approach above tends to be easier to work with when things get complicated.

I'll encode the Animal example again:

class Animal a where     catchPhrase :: a -> String     speak       :: a -> IO ()      speak a = putStrLn (catchPhrase a)  data Cat = Cat  instance Animal Cat where     catchPhrase Cat = "Meow"  data Dog = Dog instance Animal Dog where     catchPhrase Dog = "Woof"     speak Dog = putStrLn "Rowwrlrw" 

This looks nice, doesn't it? The difficulty comes when you realize that even though it looks like OO, it doesn't really work like OO. You might want to have a list of Animals, but the best you can do right now is Animal a => [a], a list of homogeneous animals, eg. a list of only Cats or only Dogs. Then you need to make this wrapper type:

{-# LANGUAGE ExistentialQuantification #-}  data AnyAnimal = forall a. Animal a => AnyAnimal a instance Animal AnyAnimal where     catchPhrase (AnyAnimal a) = catchPhrase a     speak (AnyAnimal a) = speak a 

And then [AnyAnimal] is what you want for your list of animals. However, it turns out that AnyAnimal exposes exactly the same information about itself as the Animal record in the second example, we've just gone about it in a roundabout way. Thus why I don't consider typeclasses to be a very good encoding of OO.

And thus concludes this week's edition of Way Too Much Information!

like image 141
luqui Avatar answered Sep 20 '22 03:09

luqui


It sounds like you might want to read up on typeclasses.

like image 33
Jack Kelly Avatar answered Sep 22 '22 03:09

Jack Kelly