Hello,
I have created an RFC for proper algebraic data types and structural pattern matching a la Swift or Rust. It's available here: https://github.com/nim-lang/RFCs/issues/525
I go into much more detail on the RFC itself, but here is an example of code it would allow you to write:
# An implementation of the simple lambda calculus
# (typechecker and Type union omitted)
type Ident = string
type Expr = ref union
Constant: Term
Variable: tuple[id: Ident]
# Annotation: tuple[expr: Expr, kind: Type]
Abstraction: tuple[param: Ident, fn: Expr]
Application: tuple[fn, arg: Expr]
Conditional: tuple[cond, `if`, `else`: Expr]
type Term = union
Unittype Ident = string
type Expr = ref union
Constant: Term
Variable: tuple[id: Ident]
# Annotation: tuple[expr: Expr, kind: Type]
Abstraction: tuple[param: Ident, fn: Expr]
Application: tuple[fn, arg: Expr]
Conditional: tuple[cond, `if`, `else`: Expr]
type Term = union
Unit
Boolean: bool
Natural: uint
Integer: int
Float: float
String: tuple[len: uint, cap: uint, data: seq[uint]]
Enum: tuple[val: uint, data: seq[Type]]
Struct: Table[Ident, Term]
Function: Expr
var globalSymbolTable = initTable[Ident, Term]()
func execute(ast: Expr): Term =
case ast
of Constant(term):
return term
of Variable(id):
return globalSymbolTable[id]
# other structures of applications + abstractions are invalid
of Application(Abstraction(param, fn), arg):
globalSymbolTable[param] = execute(arg)
return execute(fn)
of Conditional(cond, `if`: ifClause, `else`: elseClause):
if execute(cond) == Boolean(true):
return execute(ifClause)
else:
return execute(elseClause)
else:
echo "Failed to execute AST! Improper structure"
quit()
Integer: int
Float: float
String: tuple[len: uint, cap: uint, data: seq[uint]]
Enum: tuple[val: uint, data: seq[Type]]
Struct: Table[Ident, Term]
Function: Expr
var globalSymbolTable = initTable[Ident, Term]()
func execute(ast: Expr): Term =
case ast
of Constant(term):
return term
of Variable(id):
return globalSymbolTable[id]
# other structures of applications + abstractions are invalid
of Application(Abstraction(param, fn), arg):
globalSymbolTable[param] = execute(arg)
return execute(fn)
of Conditional(cond, `if`: ifClause, `else`: elseClause):
if execute(cond) == Boolean(true):
return execute(ifClause)
else:
return execute(elseClause)
else:
echo "Failed to execute AST! Improper structure"
quit()
Please let me know what you think of this proposal on the Github issue or here in the forum.
Very interesting, actually when I was using Rust I kind of missed Haskell pattern matching.
Regarding this
type Term = union
Unittype Ident = string
This seems "unnatural", a bit similar to Rust Phantom data types, I don't see this syntax in the RFC either, something that you changed midway?
It seems "unnatural" because it is complete garbage! Middle-click paste strikes again, that is the same code block accidentally pasted into itself.
I can't seem to edit my post any more as it's too old, shame. If you have post edit privileges would you mind removing lines 13 through 24 of the code block?
@J-James has resolved the var/non-var by just using the mutability of the variable, if you match on a mutable variable it's mutable, and same for immutable.
There's some ways to one line if/else matches but they're all kinda hacky.
What you think if (circ: Circle) from shape is hacky?! It's hardly readable! :P
What you think if (circ: Circle) from shape is hacky?! It's hardly readable! :P
Never! :sweat: ;)
Yeah, I'd really like an equivalent of Rust's if let: but not if let! I hate the syntax there too :-D
Definitely taking suggestions there (and everywhere, but especially there). My current thought is if Circle(binding) == variable but I don't really like it either: it's clean but very much breaks with expected behavior. Perhaps something like if circ == Circle then Circle(binding)? But I don't like that either...
I would argue with you there: computer science terminology is far from standardized (just look at dynamic arrays! every language uses a totally different name). I would consider the implementation of this to be a tagged union, and in type theory enum is a standard synonym for sum types.
I've yet to fully delve into the implementations of this in other languages: but I suspect this implementation will also have to fulfill the occupies-the-same-space-in-memory part of some definitions of unions for type safety reasons. Also making sum a keyword would cause significantly more issues than using union.
The word “union” has a standard meaning in mathematics. And is there any language other than Rust that calls sum types “enums”?
Yeah, sum would be bad as a keyword. Perhaps variant?
The word unions / tagged unions / discriminated unions have a standard meaning in computer science. It's fine because with context it's hard to confuse the two.
Using modulo for remainder is a way worse offense because both are used in the same context.
For example the term field has a very definite meaning in the field of mathematics that is different from a magnetic field in the field of physics ...
Yah Rusts if let … just feels so ugly to me.
Perhaps something like if circ as Circle: or if circ into Circle:. I think those aren’t possible to implement as a library feature though IIRC. Just overshadowing the name simplifies the syntax a lot.
Maybe even an operator if it was standardized in the language if cir: Circle <- obj: or if cir: Circle =~ obj:. Both of those build off idioms in other languages.