emdash / udlang

A practical, functional language for stream processing.
GNU Lesser General Public License v3.0
1 stars 0 forks source link

Should uDLang model ADTs with Rust-style enums, or ML-style unions? #24

Open emdash opened 3 years ago

emdash commented 3 years ago

The choice has implications for pattern matching syntax and serialization/de-serialization.

Thoughts on unions, atoms, and enumerations in type theory.

uDLang's type system is inspired by FlowJS. FlowJS types are set-theoretic, i.e. they are conceptually sets of values. I'm not sure if FlowJS is fully-dependent. But it has some dependent-like features, like "string enums", which use the syntax for unions.

FlowJS relies on type refinements, because it cannot extend JS's syntax. Whereas uDLang will use explicit pattern matching via the match construct.

The overlap is that

Related Features

Atom types

An atom is an opaque named value. Generally something that can be silently converted to an an integer constant (like a string hash). They can be used as tags to distinguish ADT variants.

Explicit ADTs

This is a feature that can be implemented separately, or as syntax sugar on top of sum types. I will probably chose the latter now that I've made the decision to focus on msgpack.

Destructuring

Patterns used for de-structuring are also useful in match expressions, and should have the same meaning.

[x, y] = p;
{foo, bar: [baz, quux]} = in.objects;
match (in.objects) {
   case [x, y]: ....;
   case {foo, bar: [baz, quux]}: ....;
}

Spread Syntax

The spread syntax would also be useful in patterns. Where ES6. ES6 is permissive, uDLang is strict.

[x, y, ...rest] = p;  // take first two values from p, bind a reference to any remaining
[x, y, ...] = p;        // take the first two values from p, and discard the rest.
[x, y] = p;             // error, unless p has exactly two elements.
{x, y} = o;             // error, unless p has exactly the fields x and y;
{x, y, ...} = 0;        // extract x and y, discard the rest.
{x, y, ...rest} = o;  // extract x, and y, bind the difference to ...rest;
{x: int, y: float} = o; // error unless value of x is int, and value of y is float.

In a match expression, rather than being treated as errors, matches that fail simply don't execute.

Order of Matching

From a correctness standpoint, there'd be some sound formal notion of pattern specificity, and the implementation would always try patterns in order of most to least specific, or something along those lines, so that match arms don't accidentally shadow one another.

For MVP, matches will be tried sequentially, with the first one that matches "winning". If I can also get exhaustivity analysis on top of that, I'll be pretty happy.

Other models