Revision as of 06:47, 22 February 2008

Contents

1 The problem

Does some kind of collection of objects with different types in Haskell
exist? Obviously, tuples are an example, but they have a fixed length.
To compare tuples vs lists:

Tuples

Lists

Heterogeneous

Homogeneous

Fixed length (per tuple type)

Variable length

Always finite

May be infinite

However, the need is for heterogeneous and non-fixed length. When one is
used to Java, with its loose typing of collections,not having this
immediately and easily available seems strange. As an example, the need
is for something like LinkedList<Object> from Java.

2 Algebraic datatypes

If the number of types to cover is fixed, then the problem can be
solved by a list of data types such as

This is a very basic solution, and often preferable. Limitations: You
have to type-switch all the time if you want to do anything with the
objects in the List, and the collections are clumsy to extend by new
types.

3 A Universal type

Similar to the Object type in Java, the

Dynamic

type in

Haskell can be used to wrap any type in the Typeable class, creating a
suitable wrapper:

Depending on needs and comfort level with fancier types, the existential
approach to ADTs might solve the problem. The types aren't that scary.

This is example akin to upcasting in Java to an interface that lets you print
things. That way you know how to print every object (or do whatever else it is
you need to do) in the list. Beware: there is no safe downcasting (that's what
Typeable would be for); that would likely be more than you need.

Essentially existential values pack up a value with operations on that value,
and hide the actual value's types. Thus objects of differing types can be used,
as long as they all provide a common interface.

The most convenient way to pack a value with its methods is to use a typeclass
dictionary. The typeclass declaration defines the api to be wrapped with each
value. You can also pack up your own interface as an explicit field in the data
type, if you want to avoid type classes.

{-# OPTIONS -fglasgow-exts #-}---- An existential type encapsulating types that can be Shown-- The interface to the type is held in the show method dictionary---- Create your own typeclass for packing up other interfaces--data Showable =forall a .Show a => MkShowable a
---- And a nice existential builder--
pack ::Show a => a -> Showable
pack = MkShowable
---- A heteoregenous list of Showable values--
hlist ::[Showable]
hlist =[ pack 3, pack 'x', pack pi, pack "string", pack (Just ())]---- The only thing we can do to Showable values is show them--
main ::IO()
main =print$map f hlist
where
f (MkShowable a)=show a
{-
*Main> main
["3","'x'","3.141592653589793","\"string\"","Just ()"]
-}

One can of course make the type

Showable

an instance of the type class

Show

itself

---- Make Showable itself an instance of Show--instanceShow Showable
where
showsPrec p (MkShowable a)= showsPrec p a
---- The only thing we can do to Showable values is show them--
main ::IO()
main =print hlist
{-
*Main> main
[3,'x',3.14159265358979,"string",Just ()]
-}

Note how we didn't need to unwrap and show the values explicitly ourselves.

There's an alternative way of defining an existential datatype, using GADT syntax. Instead of writing

data Showable =forall a .Show a => MkShowable a

one writes

data Showable
where
MkShowable ::Show a => a -> Showable

i.e. giving an explicit type signature for the

MkShowable

data constructor.
(Using explicit

forall a.

before the

Show a =>

part is allowed, but not required, just as for ordinary type signatures.)

5 HLists, OOHaskell, type-level programming

This is the cleanest solution, but very advanced and a little restrictive.
Read these two articles: