In order to prove that for instance the Category laws hold for some operations on a data type, how do one decide how to define equality? Considering the following type for representing boolean expressions:
data Exp
= ETrue
| EFalse
| EAnd Exp Exp
deriving (Eq)
Is it feasible trying to prove that Exp forms a Category with identity ETrue and operator:
(<&>) = EAnd
without redefining the Eq instance? Using the default instance of Eq the left-identity law breaks, i.e:
ETrue <&> e == e
evaluates to False. However, defining an eval function:
eval ETrue = True
eval EFalse = False
eval (EAnd e1 e2) = eval e1 && eval e2
and the Eq instance as:
instance Eq Exp where
e1 == e2 = eval e1 == eval e2
fixes the problem. Is comparison in terms of (==) a general requirement for claiming to satisfy such laws, or is it sufficient to say that the laws hold for a particular type of equality operator?
Equality is EVIL. You rarely (if ever) need structural equality,
because it is too strong. You only want an equivalence that is strong enough for
what you're doing. This is particularly true for category theory.
In Haskell, deriving Eq will give you structural equality, which means that you'll
often want to write your own implementation of == / /=.
A simple example: Define rational number as pairs of integers,
data Rat = Integer :/ Integer. If you use structural equality (what Haskell is
deriving), you'll have (1:/2) /= (2:/4), but as a fraction 1/2 == 2/4. What
you really care about is the value that your tuples denote, not their
representation. This means you'll need an equivalence that compares reduced
fractions, so you should implement that instead.
Side note: If someone using the code assumes that you've defined a structural
equality test, i.e. that checking with == justifies replacing data sub-components
through pattern matching, their code may break. If that is of importance,
you may hide the constructors to disallow pattern matching, or maybe define your
own class (say, Equiv with === and =/=) to separate both concepts. (This
is mostly important for theorem provers like Agda or Coq, in Haskell it's really
hard to get practical/real-world code so wrong that finally something breaks.)
Really Stupid(TM) example: Let's say that person wants to print long lists of huge
Rats and believes memoizing the string representations of the Integers will save
on binary-to-decimal conversion. There's a lookup table for Rats, such that equal
Rats will never be converted twice, and there's a lookup table for integers. If
(a:/b) == (c:/d), missing integer entries will be filled by copying between a-c /
b-d to skip conversion (ouch!). For the list [ 1:/1, 2:/2, 2:/4 ], 1 gets
converted and then, because 1:/1 == 2:/2, the string for 1 gets copied into the
2 lookup entry. The final result "1/1, 1/1, 1/4" is borked.
Related
Imagine a language which doesn't allow multiple value constructors for a data type. Instead of writing
data Color = White | Black | Blue
we would have
data White = White
data Black = Black
data Blue = Black
type Color = White :|: Black :|: Blue
where :|: (here it's not | to avoid confusion with sum types) is a built-in type union operator. Pattern matching would work in the same way
show :: Color -> String
show White = "white"
show Black = "black"
show Blue = "blue"
As you can see, in contrast to coproducts it results in a flat structure so you don't have to deal with injections. And, unlike sum types, it allows to randomly combine types resulting in greater flexibility and granularity:
type ColorsStartingWithB = Black :|: Blue
I believe it wouldn't be a problem to construct recursive data types as well
data Nil = Nil
data Cons a = Cons a (List a)
type List a = Cons a :|: Nil
I know union types are present in TypeScript and probably other languages, but why did the Haskell committee chose ADTs over them?
Haskell's sum type is very similar to your :|:.
The difference between the two is that the Haskell sum type | is a tagged union, while your "sum type" :|: is untagged.
Tagged means every instance is unique - you can distunguish Int | Int from Int (actually, this holds for any a):
data EitherIntInt = Left Int | Right Int
In this case: Either Int Int carries more information than Int because there can be a Left and Right Int.
In your :|:, you cannot distinguish those two:
type EitherIntInt = Int :|: Int
How do you know if it was a left or right Int?
See the comments for an extended discussion of the section below.
Tagged unions have another advantage: The compiler can verify whether you as the programmer handled all cases, which is implementation-dependent for general untagged unions. Did you handle all cases in Int :|: Int? Either this is isomorphic to Int by definition or the compiler has to decide which Int (left or right) to choose, which is impossible if they are indistinguishable.
Consider another example:
type (Integral a, Num b) => IntegralOrNum a b = a :|: b -- untagged
data (Integral a, Num b) => IntegralOrNum a b = Either a b -- tagged
What is 5 :: IntegralOrNum Int Double in the untagged union? It is both an instance of Integral and Num, so we can't decide for sure and have to rely on implementation details. On the other hand, the tagged union knows exactly what 5 should be because it is branded with either Left or Right.
As for naming: The disjoint union in Haskell is a union type. ADTs are only a means of implementing these.
I will try to expand the categorical argument mentioned by #BenjaminHodgson.
Haskell can be seen as the category Hask, in which objects are types and morphisms are functions between types (disregarding bottom).
We can define a product in Hask as tuple - categorically speaking it meets the definition of the product:
A product of a and b is the type c equipped with projections p and q such that p :: c -> a and q :: c -> b and for any other candidate c' equipped with p' and q' there exists a morphism m :: c' -> c such that we can write p' as p . m and q' as q . m.
Read up on this in Bartosz' Category Theory for Programmers for further information.
Now for every category, there exists the opposite category, which has the same morphism but reverses all the arrows. The coproduct is thus:
The coproduct c of a and b is the type c equipped with injections i :: a -> c and j :: b -> c such that for all other candidates c' with i' and j' there exists a morphism m :: c -> c' such that i' = m . i and j' = m . j.
Let's see how the tagged and untagged union perform given this definition:
The untagged union of a and b is the type a :|: b such that:
i :: a -> a :|: b is defined as i a = a and
j :: b -> a :|: b is defined as j b = b
However, we know that a :|: a is isomorphic to a. Based on that observation we can define a second candidate for the product a :|: a :|: b which is equipped with the exact same morphisms. Therefore, there is no single best candidate, since the morphism m between a :|: a :|: b and a :|: b is id. id is a bijection, which implies that m is invertible and "convert" types either way. A visual representation of that argument. Replace p with i and q with j.
Restricting ourselves Either, as you can verify yourself with:
i = Left and
j = Right
This shows that the categorical complement of the product type is the disjoint union, not the set-based union.
The set union is part of the disjoint union, because we can define it as follows:
data Left a = Left a
data Right b = Right b
type DisjUnion a b = Left a :|: Right b
Because we have shown above that the set union is not a valid candidate for the coproduct of two types, we would lose many "free" properties (which follow from parametricity as leftroundabout mentioned) by not choosing the disjoint union in the category Hask (because there would be no coproduct).
This is an idea I've thought a lot about myself: a language with “first-class type algebra”. Pretty sure we could do about everything this way that we do in Haskell. Certainly if these disjunctions were, like Haskell alternatives, tagged unions; then you could directly rewrite any ADT to use them. In fact GHC can do this for you: if you derive a Generic instance, a variant type will be represented by a :+: construct, which is in essence just Either.
I'm not so sure if untagged unions would also do. As long as you require the types participating in a sum to be discernibly different, the explicit tagging should in principle not be necessary. The language would then need a convenient way to match on types at runtime. Sounds a lot like what dynamic languages do – obviously comes with quite some overhead though.
The biggest problem would be that if the types on both sides of :|: must be unequal then you lose parametricity, which is one of Haskell's nicest traits.
Given that you mention TypeScript, it is instructive to have a look at what its docs have to say about its union types. The example there starts from a function...
function padLeft(value: string, padding: any) { //etc.
... that has a flaw:
The problem with padLeft is that its padding parameter is typed as any. That means that we can call it with an argument that’s neither a number nor a string
One plausible solution is then suggested, and rejected:
In traditional object-oriented code, we might abstract over the two types by creating a hierarchy of types. While this is much more explicit, it’s also a little bit overkill.
Rather, the handbook suggests...
Instead of any, we can use a union type for the padding parameter:
function padLeft(value: string, padding: string | number) { // etc.
Crucially, the concept of union type is then described in this way:
A union type describes a value that can be one of several types.
A string | number value in TypeScript can be either of string type or of number type, as string and number are subtypes of string | number (cf. Alexis King's comment to the question). An Either String Int value in Haskell, however, is neither of String type nor of Int type -- its only, monomorphic, type is Either String Int. Further implications of that difference show up in the remainder of the discussion:
If we have a value that has a union type, we can only access members that are common to all types in the union.
In a roughly analogous Haskell scenario, if we have, say, an Either Double Int, we cannot apply (2*) directly on it, even though both Double and Int have instances of Num. Rather, something like bimap is necessary.
What happens when we need to know specifically whether we have a Fish? [...] we’ll need to use a type assertion:
let pet = getSmallPet();
if ((<Fish>pet).swim) {
(<Fish>pet).swim();
}
else {
(<Bird>pet).fly();
}
This sort of downcasting/runtime type checking is at odds with how the Haskell type system ordinarily works, even though it can be implemented using the very same type system (also cf. leftaroundabout's answer). In contrast, there is nothing to figure out at runtime about the type of an Either Fish Bird: the case analysis happens at value level, and there is no need to deal with anything failing and producing Nothing (or worse, null) due to runtime type mismatches.
Suppose I want to define the type Mod4 of integers modulo 4. After all, Int is Mod2^64. One obvious way I could go is
data Mod4 = ZeroMod4 | OneMod4 | TwoMod4 | ThreeMod4
However I could also do this
data Mod4 = Mod4 Integer
instance Eq Mod4 where
(Mod4 x) == (Mod4 y) = (x-y) `mod` 4 == 0
But then this function is problematic :
f :: Mod4 -> Mod4
f (Mod4 x) = if x < 20 then Mod4 0 else Mod4 1
f (Mod4 16) is different from f (Mod4 20), whereas those two arguments are ==. So I end up with two sorts of equality : representation in memory (
Mod4 16 is different from Mod4 20) and ==.
Since all functions can pattern match their arguments, they can always bypass any == operator. Why didn't Haskell just took the representation in memory as the definition of equality ? This way all types become trivially equatable.
Actually, equality is implied by the very concept of function : a graph that produces equal outputs when given equal inputs. So it makes little sense to speak of a function on a type that is not equatable.
Why didn't Haskell just took the representation in memory as the definition of equality ? This way all types become trivially equatable.
Nope. You can't compare values of type Integer -> Bool. Functions can not be compared, in general.
Back to the blackboard. How to design equality in a typed language?
One option is to let (==) :: a -> a -> Bool, and throw an exception if a is a function. See e.g. Ocaml.
Another option is to partition types in equatable/not equatable. This is eqtype in SML.
Another, but related, option is to express "eq-ability" as a constraint on the polymorphism. Eq in Haskell.
Now, Eq might have been more special. E.g. you can't define its instances by yourself, and you must use deriving Eq, similarly to how Typeable works now.
The Haskell designers instead to allow users to define their own comparison function. The users might know some "smarter" way. E.g. to compare a 10-field record, start by comparing the usually-different fields, and compare usually-equal ones later, trying to improve efficiency.
Note that, if we don't export the data type constructor, we can make equality to be an equivalence and still be useful. E.g. Data.Set.Set equates different (balanced) trees when they represent the same set, yet the exported interface never breaks the equivalence, so equality looks like equality from outside.
So it makes little sense to speak of a function on a type that is not equatable.
True, when "not equatable" is interpreted in a mathematical sense. However. when it is interpreted as "the equality predicate is not computable", it makes a lot of sense. We can speak of a function working on values whose type has undecidable equality.
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?
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.
There are some predefined versions in HaXml package with OneOfN, TwoOfN, .. constructors.
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.
Such a type is usually called a sum, variant, union, or tagged union type. Because the capability is a built-in feature of data types in Haskell, there's no name for it widely used in Haskell code. The Report only calls them "algebraic datatypes" (usually abbreviated to ADT), so that's the name you'll see most often in comments, but this name includes types with only one data constructor, which are only sum types in the trivial sense.
Say I have a general recursive definition in haskell like this:
foo a0 a1 ... = base_case
foo b0 b1 ...
| cond1 = recursive_case_1
| cond2 = recursive_case_2
...
Can it always rewritten using foldr? Can it be proved?
If we interpret your question literally, we can write const value foldr to achieve any value, as #DanielWagner pointed out in a comment.
A more interesting question is whether we can instead forbid general recursion from Haskell, and "recurse" only through the eliminators/catamorphisms associated to each user-defined data type, which are the natural generalization of foldr to inductively defined data types. This is, essentially, (higher-order) primitive recursion.
When this restriction is performed, we can only compose terminating functions (the eliminators) together. This means that we can no longer define non terminating functions.
As a first example, we lose the trivial recursion
f x = f x
-- or even
a = a
since, as said, the language becomes total.
More interestingly, the general fixed point operator is lost.
fix :: (a -> a) -> a
fix f = f (fix f)
A more intriguing question is: what about the total functions we can express in Haskell? We do lose all the non-total functions, but do we lose any of the total ones?
Computability theory states that, since the language becomes total (no more non termination), we lose expressiveness even on the total fragment.
The proof is a standard diagonalization argument. Fix any enumeration of programs in the total fragment so that we can speak of "the i-th program".
Then, let eval i x be the result of running the i-th program on the natural x as input (for simplicity, assume this is well typed, and that the result is a natural). Note that, since the language is total, then a result must exist. Moreover, eval can be implemented in the unrestricted Haskell language, since we can write an interpreter of Haskell in Haskell (left as an exercise :-P), and that would work as fine for the fragment. Then, we simply take
f n = succ $ eval n n
The above is a total function (a composition of total functions) which can be expressed in Haskell, but not in the fragment. Indeed, otherwise there would be a program to compute it, say the i-th program. In such case we would have
eval i x = f x
for all x. But then,
eval i i = f i = succ $ eval i i
which is impossible -- contradiction. QED.
In type theory, it is indeed the case that you can elaborate all definitions by dependent pattern-matching into ones only using eliminators (a more strongly-typed version of folds, the generalisation of lists' foldr).
See e.g. Eliminating Dependent Pattern Matching (pdf)
How can I do this in haskell?
equal(S,S) -> true;
equal(S1, S2) -> {differ, S1, S2}.
Haskell has a perfectly serviceable (==) operator for checking equality (on types for which equality is defined) so I'm assuming you're referring to something else here besides merely testing equality.
I don't know Erlang, but given that you wrote equal(S, S) my first guess would be that you want pattern matches to express equality by reusing the variable name. Unfortunately Haskell (and ML-style in general) pattern matching is less powerful than in languages like Prolog; all the pattern can do is bind variables, not perform full unification.
It's true that there are constant value patterns like foo [1,2] = ... but that's just syntactic sugar for a binding and equality check, and it's only done for constant values, not variables.
The usual Haskell approach would probably be pattern guards, like this:
data EqualResult a b = Yep | Nope (a, b) deriving (Show, Eq)
equal :: (Eq a) => a -> a -> EqualResult a a
equal s1 s2 | s1 == s2 = Yep
| otherwise = Nope (s1, s2)
On the off chance that you wanted some sort of reference equality instead of checking for equal values, that doesn't work because it doesn't even make sense in Haskell.
Edit: It has been pointed out to me that you may also have been asking about returning different result types. Working with types should be covered well in any introduction to Haskell, but the short version in this case is that if you need to return one of two possible types, you need a data type with one constructor for each; you then examine the result using pattern matching (in a declaration or case expression).
In this case, to make it look more like your function I've made a special-purpose type with two constructors: One indicating equality (with no further details) and one indicating inequality that holds a pair of values. You can also do it in a generic way using the built-in type Either a b, which has two constructors Left a and Right b.