The relational model adds constraints, state, and a mechanism for first-class derived relations (updateable views).
And while you can stick anything with a well-defined equality in a relation, including other relations, the point of the relational algebra is to describe structure using relations, thus making it all accessible to relational operators. In a properly normalized database, all structure can be manipulated through a common set of operations.
So you could, e.g. create a relation with a single JSON attribute and call it a day. But now, in addition to the relational operators, you need a whole mess of JSON operators to query it.
Thus, while you could have a sum type, you don't need this because you can put the various summands into separate relations. For instance, the simple case of booleans:
Persons(key id: int, name: str, is_tall: bool)
... noramlizes to ...
Persons(key id: int, name: str)
TallPersons(key id: int)
Or for an Either: Persons(key id: int, name: str, zing: either<int, str>)
... noramlizes to ...
Persons(key id: int, name: str)
LeftPersons(key id: int, zing: int)
RightPersons(key id: int, zing: str)
AssertEmpty: LeftPersons{key} & RightPersons{key}
What you really want your database to do is to let you enter that first "Persons" table with the sum type. That should logically be a derived table that is backed by the normalized tables.Then, you'd get the simplicity of entering Persons.insert(key=5, name='bob', zing=Left(5)), but that's simply an updateable view. It will really update the base tables Persons / LeftPersons with simple atomic values.