Reputation: 25
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.
Upvotes: 0
Views: 2586
Reputation: 139681
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 instantiatesEq
, 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 typeSB :: forall s. Show s => s -> ShowBox
. If we were now writing a function that we were intending to passheteroList
, we couldn't apply any functions like not to the values inside theSB
because they might not beBool
s. 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 ofs
. But as we mentioned, we do know that the type is an instance ofShow
due to the constraint on theSB
constructor. Therefore, it's legal to use the function show ons
, as seen in the right-hand side of the function definition.As for
f
, recall the type ofExample: 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 ofShow
, 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.
Upvotes: 4
Reputation: 64750
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
Upvotes: 7
Reputation: 13497
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.
Upvotes: 13
Reputation: 370415
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 ListObject
s 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]
.
Upvotes: 11