Translate to RA: bi-implication/equivalence - logic

(No this isn't one of those translate SQL to RA questions ;-) I have a formula in First-Order Logic that I want to express in RA. That ought to be easy: Codd's 1972 approach in the Relational Completeness paper is to show each FOL operator can be equivalently expressed in RA.
Given relation SP:
Heading {S# CHAR, P# CHAR, QTY INT}
Key {S#, P#}
Characteristic predicate SP(s, p, q) = 'Supplier s supplies Part p in quantity q.'
Express: 'Supplier 'S1' and Supplier 'S2' supply exactly the same set of Parts (disregarding quantities).'
Formula:
∀p. (∃q1. SP('S1', p, q1) ) ⇔ (∃q2. SP('S2', p, q2) )
Note in case of S1 supplying no parts at all, this formula is true just in case S2 also supplies no parts.
This is a Yes/No question (the formula has no free variables); so I'd expect the RA expression must result in a relation with no attributes, returning an empty relation if the two Suppliers do not supply the same set of Parts (formula evaluates to False); otherwise the non-empty relation with no attributes (formula evaluates to True).
To explain a bit further: usually queries return a list of something -- such as the list of Parts supplied by S1, disregarding quantities: SP WHERE (S# = 'S1') {P#} (or in Greek π{P#}(σS# = 'S1'(SP))). For a Yes/No question, we're interested only in whether the query returns something vs nothing, e.g. does Supplier S1 supply Part P456?: SP WHERE (S# = 'S1' AND P# ='P456') {} (π{}(σS# = 'S1'(σP# = 'P456'(SP)))).
You'll notice I'm using a variant of RA: Tutorial D from Date & Darwen. This is easier to read and typeset than Codd's original RA (I've also included the Greek characters and subscripts form). I'll limit myself to Tutorial D operators that correspond to Codd's RA.
I can do the inverse of the query I want: 'Are there any Parts Supplied by S1 but not by S2, or vice versa?'
Firstly a couple of shorthands for common subexpressions
WITH S1P := SP WHERE (S# = 'S1'){P#},
S2P := SP WHERE (S# = 'S2'){P#} :
( S1P MINUS S2P )
UNION
( S2P MINUS S1P );
For those who prefer Greek:
S1P := π{P#}(σS# = 'S1'(SP))
S2P := π{P#}(σS# = 'S2'(SP))
(S1P \ S2P) ∪ (S2P \ S1P)
This'll return an empty result just in case the two Suppliers supply exactly the same set of Parts. So all that remains to do is project that result on to no attributes, and flip empty result to non-empty and vice versa. But Codd's RA doesn't have a way to express that flip, AFAICT.
Applying Codd's 1972 method to the formula, the outermost operation is a forall quantifier, so convert that to a negation of an existential:
¬∃p. ¬( (∃q1. SP('S1', p, q1) ) ⇔ (∃q2. SP('S2', p, q2) ) )
But now the outermost operation is negation. Codd's method only allows negation to appear nested inside conjunction.
I'm stuck. Any ideas?

There is no RA expression that answers the question, if we limit to RA operators and semantics per Codd's 1972 specification.
Even if we add the operators commonly included in RA these days, we can't answer the question as posed. For example, the operators covered in wikipedia such as Rename aka ρ, Extend (for calculated columns), Grouping/Aggregation, Outer Joins.
From the discussion, arguably, the desired result (a degree-zero relation) is not countenanced by Codd. I say "arguably" because Codd never rigorously defines 'relation'. There's Codd 1970 footnote 1 "R is a subset of the Cartesian product S1 x S2 x ... x Sn."; but no lower bound given for n. Clearly it's intended to include the degenerate 'product' for n is 1, then why not allow zero?
For example SQL does not support degree-zero tables. SQL does support pseudo-extending a would-be degree-zero table with a dummy column:
SELECT 'Yes' AS Dummy FROM SP WHERE...
Even allowing that, I claim the question as posed can't be answered in SQL. (Consider the case where SP is empty: then the two Suppliers do supply the same set of Products, viz. the empty set; but the FROM SP ... can only return an empty relation.)
Various non-standard operators or primitives have been suggested (see Comments on q and on other answers). AFAICT there is no authoritative reference that 'blesses' any particular approach. For example, the Alice Book seems not to consider relations of degree zero.
To briefly survey the possible operators/primitives. (Any one of these is expressively equivalent to any other, in the sense that if we have one we can define the others in terms of it -- except for the last.)
Those returning true/false:
Relational comparison: subset ⊆, which can be used to define equality of relations ==. (These require the operands to be 'Union Compatible'.)
IS_EMPTY( ) (which appears in Tutorial D).
The difficulty with returning true/false is that there are no such primitives in RA. (RA operators are usually described as "closed over relations".) Alternatively these operators could return a degree-zero relation; but then why not go to that direct?
Those returning a degree-zero relation:
A complement operation, valid only applied to a degree-zero relation. (This is the "flip" operation discussed in the q.)
Make Dee a primitive -- that is, the non-empty degree-zero relation. Then Dum =df Dee MINUS Dee; and in general complement of r (which must be degree-zero) =df Dee MINUS r
Provide primitive(s) to express a relation literal/constant value, just as most programming languages support expressing numeric or String literals, or more complex data structures. Then Dum/Dee are just two amongst the many relation constants.

Related

Is it possible to generate an equality function based on the data to be compared?

Two Booleans are equal if the're the same value, two numbers similarly. Two sets are equal if they have the same elements. In case of checking two sets for equality we can use the following scheme/racket function:
(define (same-set? l1 l2)
(and (subset? l1 l2) (subset? l2 l1)))
How would such a function be generated automatically? Can it be generated for arbitrary data type?
The basic properties of an equivalence relation are:
Substitution property: For any quantities a and b and any expression F(x), if a = b, then F(a) = F(b) (if both sides make sense, i.e. are well-formed).
Some specific examples of this are:
For any real numbers a, b, and c, if a = b, then a + c = b + c (here F(x) is x + c);
For any real numbers a, b, and c, if a = b, then a − c = b − c (here F(x) is x − c);
For any real numbers a, b, and c, if a = b, then ac = bc (here F(x) is xc);
For any real numbers a, b, and c, if a = b and c is not zero, then a/c = b/c (here F(x) is x/c).
Reflexive property: For any quantity a, a = a.
Symmetric property: For any quantities a and b, if a = b, then b = a.
Transitive property: For any quantities a, b, and c, if a = b and b = c, then a = c.
Is it possible to generate a function that obeys the above properties? Would that be enough? Could knowing the type of data help?
If you have any ideas on how to improve this question or tag it please comment.
I just want to expand on #Sorawee Porncharoenwase's answer a bit. They mentioned two kinds of equality, referential equality with eq?, and structural equality with equal?.
These different notions of equality should all follow the basic requirements of reflexivity, symmetry, and transitivity. But what sets them apart from each other is the guarantees that they give when they return true or false.
Some useful classes of equality to keep in mind are are reference-equality, structural-equality for all-time, structural-equality for the current time, and domain-specific equivalences.
Reference equality
The eq? function implements reference equality, and it has the strongest guarantees when it returns true, but when it returns false you haven't learned much.
(eq? x y) implies that x and y are literally the same object, and that any operation on x could be replaced with the same on y, including mutation. One thing that helped explain this to me was in the book Realm of Racket, saying that if you shave x, then y will also be shaved because it's the same object.
However, when (eq? x y) returns false that's pretty weak sauce. On the many data structures that involve allocating memory, eq? can return false simply because the pointers are different, even if they're immutable and everything else is the same.
This can be provided automatically by the programming language because it's really not much more than pointer-equality, and it doesn't have to generate any new behavior for new data structures.
Structural Equality for All-Time
This notion of equality is not currently well-supported by base Racket or standard Scheme, although libraries such as Rackjure can provide limited versions of this with functions like egal?. It implements reference equality on mutable data structures, but structural equality on immutable data structures.
This is meant to provide the guarantee that if (egal? x y) returns true now, then it has been true in the past and will continue to be true in the future as long as x and y both exist.
This can be provided automatically by the programming language as long as the language allows you to specify which data structures are immutable vs mutable, and enforces the immutability.
I'm not sure, but chaperone-of? may also be an example of following the ideas of "Structural Equality for All-Time", except that chaperone-of? isn't symmetric (and a naive symmetric-closure would lose transitivity).
If you want to read more, see Types of Equality in Pyret or Equal Rights for Functional Objects.
Structural Equality for the Current Time
The equal? function implements structural equality for the current time. This means two mutable data structures can be equal now if they currently have all equal components, even if they weren't equal in the past or won't be in the future due to mutation.
This can be provided automatically by the programming language as long as it always knows all the sub-parts of data contained within the data-structures.
Domain-specific Equivalences
For example for the domain of numbers and math, you might want the inexact number 2.0 to be equivalent to the exact integer 2. For the domain of string search, you might want case-insensitive equivalence for strings and characters so that A and a are equivalent. For the domain of sets, you might want order to be irrelevant so that (a b) and (b a) are equivalent.
Each domain is different, so this requires more effort on each domain. The programming language can't read your mind.
Two Booleans are equal if the're the same value, two numbers similarly. Two sets are equal if they have the same elements.
These are useful equalities, but they are not the only equalities that you can create. For instance, you can consider two numbers to be equal when their parities (odd/even) are the same. Or you can consider every number to be equal to each other.
How would such a function be generated automatically?
In general, it's not possible, because it depends on your intention. And no one can read your mind.
Is it possible to generate a function that obeys the above properties?
The answer is trivially yes. At the very least, you have (lambda (x y) #t), which says that every object is equal to every other object. It satisfies the equivalence relation properties, though it's totally useless.
For a non-trivial equality that works on all kinds of values, you have referential equality eq? which obeys the equivalence relation property (it could give you a weird result if you are using the unsafe API IIUC, but that's off-topic).
equal? can be used for structural equality on several values such as lists and those that are instances of a default transparent struct, and it also cooperates with custom equality that users provide. This is usually what you want to use in Racket.
Yes, it is definitely possible. Some programming languages allow for automatic equality function synthesis. Swift is a such example.
Without automatic synthesis, the developer has to write code for the equality, e.g., consider a struct:
struct Country: Equatable {
let name: String
let capital: String
var visited: Bool
static func == (lhs: Country, rhs: Country) -> Bool {
return lhs.name == rhs.name &&
lhs.capital == rhs.capital &&
lhs.visited == rhs.visited
}
}
With Swift 4.1 and higher, this is no longer necessary. The compiler generates the equality function for you:
struct Country: Equatable { // It's enough to just declare that the type is `Equatable` and the compiler do the rest
let name: String
let capital: String
var visited: Bool
}
Let's test it:
let france = Country(name: "France", capital: "Paris", visited: true)
let spain = Country(name: "Spain", capital: "Madrid", visited: true)
if france == spain { ... } // false
Update:
Even after Swift 4.1, it's possible to override the default implementation with own, custom logic. For example:
struct Country: Equatable {
let name: String
let countryCode: String
let capital: String
var visited: Bool
static func == (lhs: Country, rhs: Country) -> Bool {
return lhs.countryCode == rhs.countryCode
}
}
So, the developer is always in control. The equality won't be synthesised automatically, the developer has to add Equatable to the struct declaration. If after that they're not satisfied with the default implementation, or if it couldn't be inferred, there is always an option to override compiler's intention and provide a customized variant.

Relational Algebra: Natural Join having the same result as Cartesian product

I am trying to understand what will be the result of performing a natural join
between two relations R and S, where they have no common attributes.
By following the below definition, I thought the answer might be an empty set:
Natural Join definition.
My line of thought was because the condition in the 'Select' symbol is not met, the projection of all of the attributes won't take place.
When I asked my lecturer about this, he said that the output will be the same as doing a cartezian product between R and S.
I can't seem to understand why, would appreciate any help )
Natural join combines a cross product and a selection into one
operation. It performs a selection forcing equality on those
attributes that appear in both relation schemes. Duplicates are
removed as in all relation operations.
There are two special cases:
• If the two relations have no attributes in common, then their
natural join is simply their cross product.
• If the two relations have more than one attribute in common,
then the natural join selects only the rows where all pairs of
matching attributes match.
Notation: r s
Let r and s be relation instances on schema R and S
respectively.
The result is a relation on schema R ∪ S which is
obtained by considering each pair of tuples tr from r and ts from s.
If tr and ts have the same value on each of the attributes in R ∩ S, a
tuple t is added to the result, where
– t has the same value as tr on r
– t has the same value as ts on s
Example:
R = (A, B, C, D)
S = (E, B, D)
Result schema = (A, B, C, D, E)
r s is defined as:
πr.A, r.B, r.C, r.D, s.E (σr.B = s.B r.D = s.D (r x s))
The definition of the natural join you linked is:
It can be broken as:
1.First take the cartezian product.
2.Then select only those row so that attributes of the same name have the same value
3.Now apply projection so that all attributes have distinct names.
If the two tables have no attributes with same name, we will jump to step 3 and therefore the result will indeed be cartezian product.

How to find the intersection of two NFA

In DFA we can do the intersection of two automata by doing the cross product of the states of the two automata and accepting those states that are accepting in both the initial automata.
Union is performed similarly. How ever although i can do union in NFA easily using epsilon transition how do i do their intersection?
You can use the cross-product construction on NFAs just as you would DFAs. The only changes are how you'd handle ε-transitions. Specifically, for each state (qi, rj) in the cross-product automaton, you add an ε-transition from that state to each pair of states (qk, rj) where there's an ε-transition in the first machine from qi to qk and to each pair of states (qi, rk) where there's an ε-transition in the second machine from rj to rk.
Alternatively, you can always convert the NFAs into DFAs and then compute the cross product of those DFAs.
Hope this helps!
We can also use De Morgan's Laws: A intersection B = (A' U B')'
Taking the union of the compliments of the two NFA's is comparatively simpler, especially if you are used to the epsilon method of union.
There is a huge mistake in templatetypedef's answer.
The product automaton of L1 and L2 which are NFAs :
New states Q = product of the states of L1 and L2.
Now the transition function:
a is a symbol in the union of both automatons' alphabets
delta( (q1,q2) , a) = delta_L1(q1 , a) X delta_L2(q2 , a)
which means you should multiply the set that is the result of delta_L1(q1 , a) with the set that results from delta_L2(q1 , a).
The problem in the templatetypedef's answer is that the product result (qk ,rk) is not mentioned.
Probably a late answer, but since I had the similar problem today I felt like sharing it. Realise the meaning of intersection first. Here, it means that given the string e, e should be accepted by both automata.
Consider the folowing automata:
m1 accepting the language {w | w contains '11' as a substring}
m2 accepting the language {w | w contains '00' as a substring}
Intuitively, m = m1 ∩ m2 is the automaton accepting the strings containing both '11' and '00' as substrings. The idea is to simulate both automata simultaneously.
Let's now formally define the intersection.
m = (Q, Σ, Δ, q0, F)
Let's start by defining the states for m; this is, as mentioned above the Cartesian product of the states in m1 and m2. So, if we have a1, a2 as labels for the states in m1, and b1, b2 the states in m2, Q will consist of following states: a1b1, a2b1, a1b2, a2b2. The idea behind this product construction is to keep track of where we are in both m1 and m2.
Σ most likely remains the same, however in some cases they differ and we just take the union of alphabets in m1 and m2.
q0 is now the state in Q containing both the start state of m1 and the start state of m2. (a1b1, to give an example.)
F contains state s IF and only IF both states mentioned in s are accept states of m1, m2 respectively.
Last but not least, Δ; we define delta again in terms of the Cartesian product, as follows: Δ(a1b1, E) = Δ(m1)(a1, E) x Δ(m2)(b1, E), as also mentioned in one of the answers above (if I am not mistaken). The intuitive idea behind this construction for Δ is just to tear a1b1 apart and consider the states a1 and b1 in their original automaton. Now we 'iterate' each possible edge, let's pick E for example, and see where it brings us in the original automaton. After that, we glue these results together using the Cartesian product. If (a1, E) is present in m1 but not Δ(b1, E) in m2, then the edge will not exist in m; otherwise we'll have some kind of a union construction.
An alternative to constructing the product automaton is allowing more complicated acceptance criteria. Ordinarily, an NFA accepts an input string when it has reached any one of a set of accepting final states. That can be extended to boolean combinations of states. Specifically, you construct the automaton for the intersection like you do for the union, but consider the resulting automaton to accept an input string only when it is in (what corresponds to) accepting final states in both automata.

About Prolog syntax

Sometimes I see terms like:
X = a:b
or
X = a-b
I can do requests like
X = Y:Z
and the compiler unifies Y with a and Z with b, as expected.
Now my answer:
Which characters (or sequence of characters) am I allowed to use to combine two Prolog atoms?!
Maybe you can give me some links with further informations about this issue.
Thanks for your help and kind regards from Germany
Which characters (or sequence of characters) am I allowed to use to combine two Prolog atoms?!
What you are asking here for, is the entire operator syntax definition of Prolog. To get the very full answer to this, please refer to the tag iso-prolog for full information how to obtain the Prolog standard ISO/IEC 13211-1.
But as a short answer to start with:
Prolog syntax consists of
functional notation, like +(a,b), plus
a dynamically redefinable operator syntax, plus
some extra.
It seems you want to know which "characters" can be used as operators.
The short answer is that you can use all atoms Op that succeed for current_op(Pri,Fix,Op). So you can ask dynamically, which operators are present:
?- current_op(Pri, Fix, Op).
Pri = 1, Fix = fx, Op = ($)
; Pri = 1150, Fix = fx, Op = (module_transparent)
; Pri = 700, Fix = xfx, Op = (=#=)
; Pri = 700, Fix = xfx, Op = (#>=)
; Pri = 700, Fix = xfx, Op = (>=)
; ... .
All those operators can be used in the specified manner, as pre-, in-, or postfix with the indicated priorities. Some of these operators are specific to SWI, and some are defined by the standard. Above, only #>= and >= are standard operators.
Most of the operators consist of the graphic characters #$&*+-./:<=>?#^~ only or of letters, digits and underscores starting with a lower case letter. There are two solo characters !; and then there are ,| which are even more special. Operator names that are different to above need quoting - you rarely will encounter them.
To see how operators nest, use write_canonical(Term).
The long answer is that you are also able to define such operators yourself. However, be aware that changing the operator syntax has often many implications that are very difficult to fathom. Even more so, since many systems differ in some rarely used configurations. For example, the system you mentioned, SWI differs in several ways.
I'd suggest to avoid defining new operators until you have learned more about the Prolog language.
let's see what's inside X = Y:Z
?- display( X = Y:Z ).
=(_G3,:(_G1,_G2))
true.
then we have a nested structure, where functors are operators.
An operator is an atom, and the rule for atom syntax says that we have 3 kind to consider:
a sequence of any printable character enclosed in single quote
a sequence of special characters only, where a special character is one of `.=:-+*/><##~? (I hope I have found all of them, from this page you can check if I forgot someone !)
a sequence of lowercase/uppercase characters or the underscore, starting with a lowercase character
edit
A functor (shorthand for function constructor, I think, but function is misleading in Prolog context) it's the symbol that 'ties' several arguments. The number of arguments is named arity. In Prolog a term is an atomic literal (like a number, or an atom), or a recursive structure, composed of a functor and a number of arguments, each being a term itself (at least 1).
Given the appropriate declaration, i.e. op/3, unary and binary terms can be represented as expressions, like that one you show.
An example of operator, using the : special char, is ':-'
member(X,[X|_]).
member(X,[_|T]) :- member(X, T).
The O.P., said (and I quote):
Sometimes I see terms like: X = a:b or X = a-b
I can do requests like X = Y:Z and the compiler unifies Y with a and Z with b, as expected.
Now my answer: Which characters (or sequence of characters) am I allowed
to use to combine two Prolog atoms?!
The short answer is Pretty much whatever you want (provided it is an atom).
The longer answer is this:
What are seeing are infix (x infix_op b), prefix (pfx_op b) and suffix (b sfx_op ) operators. Any structure with an arity of 2 can be an infix operator. Any structure with an arity of 1 can be a prefix or suffix operator. As a result, any atom may be an operator.
Prolog is parsed via a precedence driven, recursive descent parser (written in Prolog, naturally). Operators are defined and enumerated, along with their precedence and associativity in the operator/3 predicate. Associativity has to do with how the parse tree is constructed. An expression like a - b - c could be parsed as ( a - ( b - c ) ) (right-associative), or ( ( a - b ) - c ) (left-associative).
Precedence has to do with how tightly operators bind. An expression like a + b * c binds as ( a + ( b * c ) not because of associativity, but because '*'/2 (multiplication) has higher precedence that '+'/2 (addition).
You can add, remove and change operators to your heart's content. Not that this gives you a lot of room to shoot yourself in the foot by breaking prolog's syntax.
It should be noted, however, that any operator expression can also be written via ordinary notation:
a + b * c
is exactly identical to
'+'( a , '*'(b,c) )

Haskell's algebraic data types

I'm trying to fully understand all of Haskell's concepts.
In what ways are algebraic data types similar to generic types, e.g., in C# and Java? And how are they different? What's so algebraic about them anyway?
I'm familiar with universal algebra and its rings and fields, but I only have a vague idea of how Haskell's types work.
Haskell's algebraic data types are named such since they correspond to an initial algebra in category theory, giving us some laws, some operations and some symbols to manipulate. We may even use algebraic notation for describing regular data structures, where:
+ represents sum types (disjoint unions, e.g. Either).
• represents product types (e.g. structs or tuples)
X for the singleton type (e.g. data X a = X a)
1 for the unit type ()
and μ for the least fixed point (e.g. recursive types), usually implicit.
with some additional notation:
X² for X•X
In fact, you might say (following Brent Yorgey) that a Haskell data type is regular if it can be expressed in terms of 1, X, +, •, and a least fixed point.
With this notation, we can concisely describe many regular data structures:
Units: data () = ()
1
Options: data Maybe a = Nothing | Just a
1 + X
Lists: data [a] = [] | a : [a]
L = 1+X•L
Binary trees: data BTree a = Empty | Node a (BTree a) (BTree a)
B = 1 + X•B²
Other operations hold (taken from Brent Yorgey's paper, listed in the references):
Expansion: unfolding the fix point can be helpful for thinking about lists. L = 1 + X + X² + X³ + ... (that is, lists are either empty, or they have one element, or two elements, or three, or ...)
Composition, ◦, given types F and G, the composition F ◦ G is a type which builds “F-structures made out of G-structures” (e.g. R = X • (L ◦ R) ,where L is lists, is a rose tree.
Differentiation, the derivative of a data type D (given as D') is the type of D-structures with a single “hole”, that is, a distinguished location not containing any data. That amazingly satisfy the same rules as for differentiation in calculus:
1′ = 0
X′ = 1
(F + G)′ = F' + G′
(F • G)′ = F • G′ + F′ • G
(F ◦ G)′ = (F′ ◦ G) • G′
References:
Species and Functors and Types, Oh My!, Brent A. Yorgey, Haskell’10, September 30, 2010, Baltimore, Maryland, USA
Clowns to the left of me, jokers to the right (Dissecting Data Structures), Conor McBride POPL 2008
"Algebraic Data Types" in Haskell support full parametric polymorphism, which is the more technically correct name for generics, as a simple example the list data type:
data List a = Cons a (List a) | Nil
Is equivalent (as much as is possible, and ignoring non-strict evaluation, etc) to
class List<a> {
class Cons : List<a> {
a head;
List<a> tail;
}
class Nil : List<a> {}
}
Of course Haskell's type system allows more ... interesting use of type parameters but this is just a simple example. With regards to the "Algebraic Type" name, i've honestly never been entirely sure of the exact reason for them being named that, but have assumed that it's due the mathematical underpinnings of the type system. I believe that the reason boils down to the theoretical definition of an ADT being the "product of a set of constructors", however it's been a couple of years since i escaped university so i can no longer remember the specifics.
[Edit: Thanks to Chris Conway for pointing out my foolish error, ADT are of course sum types, the constructors providing the product/tuple of fields]
In universal algebra
an algebra consists of some sets of elements
(think of each set as the set of values of a type)
and some operations, which map elements to elements.
For example, suppose you have a type of "list elements" and a
type of "lists". As operations you have the "empty list", which is a 0-argument
function returning a "list", and a "cons" function which takes two arguments,
a "list element" and a "list", and produce a "list".
At this point there are many algebras that fit the description,
as two undesirable things may happen:
There could be elements in the "list" set which cannot be built
from the "empty list" and the "cons operation", so-called "junk".
This could be lists starting from some element that fell from the sky,
or loops without a beginning, or infinite lists.
The results of "cons" applied to different arguments could be equal,
e.g. consing an element to a non-empty list
could be equal to the empty list. This is sometimes called "confusion".
An algebra which has neither of these undesirable properties is called
initial, and this is the intended meaning of the abstract data type.
The name initial derives from the property that there is exactly
one homomorphism from the initial algebra to any given algebra.
Essentially you can evaluate the value of a list by applying the operations
in the other algebra, and the result is well-defined.
It gets more complicated for polymorphic types ...
A simple reason why they are called algebraic; there are both sum (logical disjunction) and product (logical conjunction) types. A sum type is a discriminated union, e.g:
data Bool = False | True
A product type is a type with multiple parameters:
data Pair a b = Pair a b
In O'Caml "product" is made more explicit:
type 'a 'b pair = Pair of 'a * 'b
Haskell's datatypes are called "algebraic" because of their connection to categorical initial algebras. But that way lies madness.
#olliej: ADTs are actually "sum" types. Tuples are products.
#Timbo:
You are basically right about it being sort of like an abstract Tree class with three derived classes (Empty, Leaf, and Node), but you would also need to enforce the guarantee that some one using your Tree class can never add any new derived classes, since the strategy for using the Tree datat type is to write code that switches at runtime based on the type of each element in the tree (and adding new derived types would break existing code). You can sort of imagine this getting nasty in C# or C++, but in Haskell, ML, and OCaml, this is central to the language design and syntax so coding style supports it in a much more convenient manner, via pattern matching.
ADT (sum types) are also sort of like tagged unions or variant types in C or C++.
old question, but no one's mentioned nullability, which is an important aspect of Algebraic Data Types, perhaps the most important aspect. Since each value most be one of alternatives, exhaustive case-based pattern matching is possible.
For me, the concept of Haskell's algebraic data types always looked like polymorphism in OO-languages like C#.
Look at the example from http://en.wikipedia.org/wiki/Algebraic_data_types:
data Tree = Empty
| Leaf Int
| Node Tree Tree
This could be implemented in C# as a TreeNode base class, with a derived Leaf class and a derived TreeNodeWithChildren class, and if you want even a derived EmptyNode class.
(OK I know, nobody would ever do that, but at least you could do it.)

Resources