Cool read - but you should be careful modeling Tables as things with an "id". The relational algebra is about sets of tuples. The fact that there is an index that one can use to make joins go faster is an (important) implementation detail but divorced from the data model.
The fact that these are sets is an important distinction and enables things like predicate pushdown and other important optimizations. Would be interesting to see a Selinger-style cost-based optimizer built in Haskell!