Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to model a polymorphic list in Haskell?

Tags:

haskell

I'm trying to model some polymorphic-type data in haskell. I understand why the following code doesn't work, but I'm hoping it illustrates what I'm trying to do. My question is: what is an idiomatic way to model this with Haskell? (You don't need to keep the input format the same if there is a better way - I don't have any existing code or data.)

data Running = Sprint | Jog deriving (Show)
data Lifting = Barbell | Dumbbell deriving (Show)

data Time   = Time   Integer deriving (Show)
data Pounds = Pounds Integer deriving (Show)

data TimedActivity    = TimedActivity Running Time deriving (Show)
data WeightedActivity = WeightedActivity Lifting Pounds deriving (Show)

class Activity a

instance Activity TimedActivity
instance Activity WeightedActivity

-- I have a list of activities
main :: IO ()
main = putStrLn $ show [ TimedActivity Sprint (Time 10)
                       , WeightedActivity Barbell (Pounds 100)
                       ]

-- I then want to apply functions to generate summaries and
-- reports from those activities, i.e.:
extractLifts :: (Activity x) => [x] -> [WeightedActivity]
extractTimes :: (Activity x) => [x] -> [TimedActivity]
like image 659
Xavier Shay Avatar asked Aug 30 '13 04:08

Xavier Shay


2 Answers

Check out http://www.haskell.org/haskellwiki/Heterogenous_collections for different techniques.

like image 105
Ankur Avatar answered Nov 20 '22 10:11

Ankur


For your specific example, you can use an Either to unify both types within the same list:

both :: [Either TimedActivity WeightedActivity]
both = [ Left $ TimedActivity Sprint (Time 10)
       , Right $ WeightedActivity Barbell (Pounds 100)
       ]

extractLifts :: [Either TimedActivity WeightedActivity] -> [WeightedActivity]
extractLifts = rights

extractTimes :: [Either TimedActivity WeightedActivity] -> [TimedActivity]
extractTimes = lefts

For more than two types, just define your own abstract data type to unify them:

data Multiple = Case1 Type1 | Case2 Type2 | ...

... and extraction functions like so:

extractCase1 :: [Multiple] -> [Type1]
extractCase1 ms = [t1 | Case1 t1 <- ms]
like image 8
Gabriella Gonzalez Avatar answered Nov 20 '22 10:11

Gabriella Gonzalez