Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using data types in Haskell

I have started this a new question as it became a follow-on from my previous question.

If I have two data types which are composed of similar constructors:

data A = X | Y | Z
data B = X | Y

is there no way I can somehow represent this as:

data A = C | Z
data B = C

data C = X | Y

if you can see what I am doing- I am trying to group the X | Y into one data type, which can then be used by multiple other data types. I cannot seem to get the compiler to allow this, or if it does, I cannot pattern-match against the X or Y, only the C??

I get the error message that C has been declared multiple times.

I thought I could maybe use types, but they do not allow multiple typings.

EDIT

Even if I declare the long way (like below), it still won't compile and says X and Y have multiple declarations:

data A = X | Y | Z
data B = X | Y
like image 534
Lethi Avatar asked Sep 09 '12 21:09

Lethi


3 Answers

Not only can't you do this, you also can't do your first option - i.e. you can't have two types in the same module that both have constructors named X and Y.

If you could do this, what should the type of X be - C, A or B? The most obvious answer would be C, but then you wouldn't be able to use it in a context where an A or a B are required (note that Haskell has no subtyping), so that would defeat the purpose of the whole construct.

The best you can do is to wrap C in a constructor of A and B, i.e.:

data A = AC C | Z
data B = BC C
data C = X | Y

Then you could wrap a C with either the AC or the BC constructor to create a value of type A or B respectively.

like image 78
sepp2k Avatar answered Nov 17 '22 03:11

sepp2k


The reason you can't do this

data A = X | Y | Z
data B = X | Y

is as follows. Say you write some code later on:

foo n = (n,X)

which builds a pair consisting of n in the first slot and X in the second slot. What type should the compiler infer? A valid type would be

foo :: a -> A -> (a,A)

since X is a constructor of type A, but equally valid is

foo :: a -> B -> (a,B)

since X is a constructor of type B. If you have two constructors with the same name, you can't infer a unique type for functions that use them. So you are disallowed from giving two constructors in the same module the same name.

like image 4
Chris Taylor Avatar answered Nov 17 '22 03:11

Chris Taylor


You can't do this:

data A = C | Z
data B = C

data C = X | Y

(As an aside, if B is identical to C, then why have B at all?)

But what you can do is something like this:

data A = A_Other C | Z
data B = B_Other C

data C = X | Y

Then you can pattern match like this:

foo :: A -> String
foo (A_Other X) = "X"
foo (A_Other Y) = "Y"
foo (        Z) = "Z"

bar :: B -> String
bar (B_Other X) = "X"
bar (B_Other Y) = "Y"

foobar :: C -> String
foobar X = "X"
foobar Y = "Y"

If that makes sense...

like image 1
MathematicalOrchid Avatar answered Nov 17 '22 04:11

MathematicalOrchid