Question

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
Was it helpful?

Solution

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.

OTHER TIPS

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.

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...

You cannot do what you want because you are declaring multiple data constructors. In

data A = X | Y | Z

You are actually introducing the type A which has 3 constructors (values) X, Y, and Z. This is why your first piece of code won't compile, it sees the same name listed as constructors for two different types! If you could do this you'd have to ask yourself is

X :: A

or

X :: B

which in a non object-oriented context is scary! So you need to provide different constructor names to share that underlying data, C.

If you want to factor this, you can do as the other posts have suggested and factored-out data in unique constructors for each datatype

data A = CForA C | Z
data B = CForB C

data C = X | Y
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top