views:

244

answers:

4

I have these two Haskell data types:

data Code 
    = Code_A | Code_B | Code C
    deriving (Eq,Show)

data ListObject = Code | Int

I need to make a list that contains ListObjects. That is both integer values and codes ([1,2, Code_A, 3]). I know it should be possible but I just can't figure the syntax for it. Haskell can do some neat stuff but its syntax sucks. Some help would be greatly appreciated.

+9  A: 

The way you defined ListObject there are exactly two values of type ListObject: Code and Value. This is somewhat similar to enum ListObject {Code, Int} in C-like languages. The costructor Code of type ListObject doesn't actually have anything to do with the type Code (same goes for Int).

So the way you defined it, a list of ListObjects would look like this: [Int, Code, Code, Int]. Of course that's not very useful.

What you probably want to do is something like this: data ListObject = CodeObject Code | IntObject Int, which says "A ListObject is either an IntObject containing an Int, or a CodeObject containing a Code".

With that definition your list may look like [IntObject 42, Code Code_A, IntObject 23].

sepp2k
+12  A: 

How about:

data Code 
     = Code_A | Code_B | Code_C
     deriving (Eq,Show)

data ListObject = ListCode Code | Value Int

objects :: [ListObject]
objects = [ListCode Code_A, Value 0]

That should do it.

Robert Massaioli
+7  A: 

Some terminology is certainly needed. In your code the tokens Code and ListObject are both "data types" and are in one name space.

A second concept is "Constructors", commands used to construct a particular instance of a data type. Constructors live in a separate name space - in your code these include the tokens Code_A, Code_B, Code_C and Code, Int.

Here's your confusion. You think you've said you want a data type called ListObject which contains data types of either Code or Int. When instead you've defined ListObject with two Null (zero content) constructors called Code and Int.

Now we get to the solution, which Robert jumped to in his answer:

data ListObject = CodeObject Code | IntObject Int

Notice the syntax:

data [DataTypeName] = [ConstructorName] [Component Data Type 1] | ...

Working out the above code in English, we have a data type ListObject with two constructors (CodeObject and IntObject), each constructor takes a single data type (Code and Int respectively) to create an instance of the ListObject type.

Stop here if you understand! Some people have an easier time (and some a harder time) with using function type signatures for constructors. The above ListObject using such notation looks like:

data ListObject where
        CodeObject :: Code -> ListObject
        IntObject  :: Int -> ListObject

This makes it clear that CodeObject is some sort of function accepting Code and returning a ListObject. Similar story with IntObject

TomMD
+2  A: 

Example: heterogeneous lists

The premise behind Haskell's typeclass system is grouping types that all share a common property. So if you know a type instantiates some class C, you know certain things about that type. For example, Int instantiates Eq, so we know that elements of Int can be compared for equality.

Suppose we have a group of values, and we don't know if they are all the same type, but we do know they all instantiate some class, i.e. we know all the values have a certain property. It might be useful to throw all these values into a list. We can't do this normally because lists are homogeneous with respect to types: they can only contain a single type. However, existential types allow us to loosen this requirement by defining a 'type hider' or 'type box':

Example: Constructing a heterogeneous list

data ShowBox = forall s. Show s => SB s

heteroList :: [ShowBox]
heteroList = [SB (), SB 5, SB True]

We won't explain precisely what we mean by that datatype definition, but its meaning should be clear to your intuition. The important thing is that we're calling the constructor on three values of different types, and we place them all into a list, so we must end up with the same type for each one. Essentially this is because our use of the forall keyword gives our constructor the type SB :: forall s. Show s => s -> ShowBox. If we were now writing a function that we were intending to pass heteroList, we couldn't apply any functions like not to the values inside the SB because they might not be Bools. But we do know something about each of the elements: they can be converted to a string via show. In fact, that's pretty much the only thing we know about them.

Example: Using our heterogeneous list

 instance Show ShowBox where
   show (SB s) = show s        -- (*) see the comment in the text below

 f :: [ShowBox] -> IO ()
 f xs = mapM_ print xs

 main = f heteroList

Let's expand on this a bit more. In the definition of show for ShowBox – the line marked with (*) see the comment in the text below – we don't know the type of s. But as we mentioned, we do know that the type is an instance of Show due to the constraint on the SB constructor. Therefore, it's legal to use the function show on s, as seen in the right-hand side of the function definition.

As for f, recall the type of print:

Example: Types of the functions involved

 print :: Show s => s -> IO () -- print x = putStrLn (show x)
 mapM_ :: (a -> m b) -> [a] -> m ()
 mapM_ print :: Show s => [s] -> IO ()

As we just declared ShowBox an instance of Show, we can print the values in the list.

Applying it to your situation, you get

{-# LANGUAGE ExistentialQuantification #-}

data ShowBox = forall a. Show a => SB a

instance Show ShowBox where
  show (SB s) = show s

data Code = Code_A | Code_B | Code_C
  deriving (Eq,Show)

l :: [ShowBox]
l = [SB Code_A, SB 3, SB Code_C, SB 0, SB "but..."]

f = mapM_ print l

Output:

ghci> f
Code_A
3
Code_C
0
"but..."

The other answers show the approach you likely want, but this is here for completeness and to offer food for thought. As the SB "but..." bit suggests, ShowBox is more permissive.

Give us more information about the problem you're working on, and we can give you more helpful suggestions that are specific to your situation.

Greg Bacon
Is a copy and paste of information that is only minorly related to what the poster needs better than saying "here's a related link"?
TomMD