Difference between revisions of "Heterogenous collections"

From HaskellWiki
Jump to: navigation, search
Line 39: Line 39:
objectString (StringObject v) = v
objectString (StringObject v) = v
main = mapM (putStrLn . objectString) [(IntObject 7), (StringObject "eight")]
main = mapM_ (putStrLn . objectString) [(IntObject 7), (StringObject "eight")]

Revision as of 19:33, 17 November 2006

This page is a very hasty and ad-hoc summary of a common discussion on the haskell-cafe list. If you find it hard to read, please complain there and somebody hopefully will come to help.

The problem

Is some kind of collection of object with different types in Haskell exist? Except the tuples, which have fixed length. I find this

  • Tuples heterogeneous, lists homogeneous.
  • Tuples have a fixed length, or at least their length is encoded in their type. That is, two tuples with different lengths will have different types.
  • Tuples always finite.

But I need something which is heterogeneous and non-fixed length. I'm used do Java, and this switch to functional languages is very strange to me. So, to be clear, I need something like LinkedList<Object> in java.

Algebraic Datatypes

If the number of types to cover is fixed, then I suggest a data type like

data T =
     ConsInt    Int
   | ConsString String
   | ConsChar   Char


data Object = IntObject Int | StringObject String

objectString :: Object -> String
objectString (IntObject v) = show v
objectString (StringObject v) = v

main = mapM_ (putStrLn . objectString) [(IntObject 7), (StringObject "eight")]

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.

HLists, OOHaskell, Type-Level Programming

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

There is also some related material here:

Existential Types

Depending on your needs and your comfort level with fancier types, the existential approach to ADTs might solve your problem. The following code is a demonstration you can cut-and-paste-and-run.

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.

There are other ways to do this with existentials (e.g. bounded existentials), but this is what came out of my head when I read your post. Existentials seems to be the "Haskellish" way to reduce a hetergenous list to a collection of objects with common operations. HList would be the Haskellish way for more static and flexible assurances.

{-# OPTIONS -fglasgow-exts #-}

module Test where

data PrintPackage = forall a . PrintPackage a (a -> String)

instance Show PrintPackage where
   show (PrintPackage val showMethod) = showMethod val

list = [ PrintPackage 3 show
      , PrintPackage "string" show
      , PrintPackage 3.4 show

main = print list