Using data types in Haskell
Asked Answered
A

4

6

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
Arvonio answered 9/9, 2012 at 21:38 Comment(3)
What you're asking for would be a subtype of A. It would not be declared with the keyword data, which creates a new type, disjoint from prior existing types. I don't think Haskell has any such feature, but I'm not au fait with all Haskell extensions.Foeman
@Gilles: No, Haskell doesn't have any subtype polymorphism. It only has parametric polymorphism, and ad-hoc polymorphism through type classes. The closest thing you can get is an existential type, but that's almost, but not quite, an entirely different thing.Category
I would put this as an answer, but because it's not quite.. You may be able to get closer to what you want by declaring a typeclass and then operations that you need for those "common things." This is a pretty common way to flip things around to solve (a version of) this problem..Microscope
B
13

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.

Brahui answered 9/9, 2012 at 21:44 Comment(2)
Just edited to say my first option doesnt work. When you say "wrap" you literally mean "include some other piece of syntax in front of C, to distinguish from the original C"?Arvonio
@Dan Yes. In my example, you'd write AC X to create a value of type A and BC X to create value of type B. X by itself can't have two different types.Brahui
L
4

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.

Ledoux answered 10/9, 2012 at 7:56 Comment(0)
A
1

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

Arnoldoarnon answered 10/9, 2012 at 11:38 Comment(0)
R
0

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
Ruche answered 10/9, 2012 at 20:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.