[Haskell-cafe] Data declaration vs type classes

Guillaume Bouchard guillaum.bouchard+haskell at gmail.com
Mon Jan 11 14:15:34 UTC 2016

In case of the data approach, `GroceryTask` and `LaundryTask` are the
same type: `Task`. Hence you can have some kind of "dynamic"
polymorphism (or "dynamic" dispatch) by storing a list of homogeneous
types (`Task`) with heterogeneous behaviors.

For example, imagine you want to store a todo list and do all task of
the todo list.

data Task = GroceryTask | LaundryTask

doTask GroceryTask = putStrLn "grocery"
doTask LaundryTask = putStrLn "laundry"

todoList :: [Task]
todoList = [GroceryTask, LaundryTask, GroceryTask]

doAllTasks :: [Task] -> IO ()
doAllTasks tasks = mapM_ doTask tasks

However, In the case of the class approach

data GroceryTask
data LaundryTask

class Task t where
    doTask :: t -> IO ()

instance Task GroceryTask where
    doTask t = putStrLn "grocery"

instance Task LaundryTask where
   doTask t = putSTrLn "laundry"

doAllTask :: [?????] -> IO ()

In this case, GroceryTask and LaundryTask are NOT the same type, hence
the "????", you cannot create a list which stores different Tasks and
returns apply

However you can still wrap them inside a sum type :

data DoableTask = DoableGrocery GroceryTask | DoableLaundry LaundryTask

instance Task DoableTask where
    doTask (DoableGrocery t) = doTask t
    doTask (DoableLaundry t) = doTask t

(Open question: is there a hack / tool / library / Template Haskell
solution to generate this kind of stuff ?)

There is other solutions, you can partially apply the doTask function,
for examples:

todoList :: [IO ()]
todoList = [doTask GroceryTask, doTask LaundryTask, doTask GroceryTask]

(Another open question, is there a simple solution to do a map over an
literal heterogeneous list to get an homogeneous one?)

Thank to laziness, this works, but can be really boring to implement.
There is other solution using existential types or heterogeneous
lists. I'm still looking for a good discussion about which one to use
when we focus on performance.

So, finally, there is no simple solution. If your type is close and
really represents a choice between a set of possibilities and that you
know you want a kind of dynamic dispatch, definitely go for the data
approach. Else, the class approach is easier to extend at the cost of
a lot of boilerplate when you want dynamic dispatch...

On Mon, Jan 11, 2016 at 12:44 PM, Lian Hung Hon <hon.lianhung at gmail.com> wrote:
> Dear all,
> Thanks for the opinions. I'll go with type classes for now, because as
> Miguel said, I want it to be open :)
> Regards,
> Hon
> On 8 January 2016 at 19:46, Imants Cekusins <imantc at gmail.com> wrote:
>> > you convert String or Text: what encoding would you use?
>> let's say, this is very specific conversion where newtypes are used a
>> lot. There are many different formats for Int (even the same type of
>> int), String may be ascii, UTF8, ISO-..., you name it.
>> using class does not make a difference re: type definition in this case.
>> _______________________________________________
>> Haskell-Cafe mailing list
>> Haskell-Cafe at haskell.org
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

More information about the Haskell-Cafe mailing list