Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the name for the contrary of Tuple or Either with more than two options?

There is a Tuple as a Product of any number of types and there is an Either as a Sum of two types. What is the name for a Sum of any number of types, something like this

data Thing a b c d ... = Thing1 a | Thing2 b | Thing3 c | Thing4 d | ...

Is there any standard implementation?

like image 906
ais Avatar asked Jan 25 '16 15:01

ais


3 Answers

Before I make the suggestion against using such types, let me explain some background.

Either is a sum type, and a pair or 2-tuple is a product type. Sums and products can exist over arbitrarily many underlying types (sets). However, in Haskell, only tuples come in a variety of sizes out of the box. Either on the other hand, can to be (arbitrarily) nested to achieve that: Either Foo (Either Bar Baz).

Of course it's easy to instead define e.g. the types Either3 and Either4 etc, in the spirit of 3-tuples, 4-tuples and so on.

data Either3 a  b  c = Left     a |     Middle b     | Right     c
data Either4 a b c d = LeftMost a | Left b | Right c | RightMost d

...if you really want. Or you can find a library the does this, but I doubt you could call it "standard" by any standards...

However, if you do define your own generic sum and product types, they will be completely isomorphic to any type that is structurally equivalent, regardless of where it is defined. This means that you can, with relative ease, nicely adapt your code to interface with any other code that uses an alternative definition.

Furthermore, it is even very likely to be beneficial because that way you can give more meaningful, descriptive names to your sum and product types, instead of going with the generic tuple and either. In fact, some people advise for using custom types because it essentially adds static type safety. This also applies to non-sum/product types, e.g.:

employment :: Bool  -- so which one is unemplyed and which one is employed?

data Empl = Employed | Unemployed
employment' :: Empl  -- no ambiguity

or

person :: (Name, Age)  -- yeah but when you see ("Erik", 29), is it just some random pair of name and age, or does it represent a person?

data Person = Person { name :: Name, age :: Age }
person' :: Person  -- no ambiguity

— above, Person really encodes a product type, but with more meaning attached to it. You can also do newtype Person = Person (Name, Age), and it's actually quite equivalent anyway. So I always just prefer a nice and intention-revealing custom type. The same goes about Either and custom sum types.

So basically, Haskell gives you all the tools necessary to quickly build your own custom types with very clean and readable syntax, so it's best if we use it not resort to primitive types like tuples and either. However, it's nice to know about this isomorphism, for example in the context of generic programming. If you want to know more about that, you can google up "scrap your boilerplate" and "template your boilerplate" and just "(datatype) generic programming".


P.S. The reason they are called sum and product types respectively is that they correspond to set-union (sum) and set-product. Therefore, the number of values (or unique instances if you will) in the set that is described by the product type (a, b) is the product of the number of values in a and the number of values in b. For example (Bool, Bool) has exactly 2*2 values: (True, True), (False, False), (True, False), (False, True).

However Either Bool Bool has 2+2 values, Left True, Left False, Right True, Right False. So it happens to be the same number but that's obviously not the case in general.

But of course this can also be said about our custom Person product type, so again, there is little reason to use Either and tuples.

like image 181
Erik Kaplun Avatar answered Oct 24 '22 21:10

Erik Kaplun


There are some predefined versions in HaXml package with OneOfN, TwoOfN, .. constructors.

like image 37
karakfa Avatar answered Oct 24 '22 19:10

karakfa


In a generic context, this is usually done inductively, using Either or

data (:+:) f g a = L1 (f a) | R1 (g a)

The latter is defined in GHC.Generics to match the funny way it handles things.

In fact, the generic approach is to break every algebraic datatype down into (:+:) and

data (:*:) f g a = f a :*: f a

along with some extra stuff. That is, it turns everything into binary sums and binary products.

In a more concrete context, you're almost always better off using a custom algebraic datatype for things bigger than pairs or with more options than Either, as others have discussed. Slightly larger tuples (triples and maybe 4-tuples) can be useful for local one-off constructs, but it's hard to see how you'd use larger general sum types as one-offs.

like image 35
dfeuer Avatar answered Oct 24 '22 19:10

dfeuer