aboutsummaryrefslogtreecommitdiff
path: root/docs/TYPES.md
diff options
context:
space:
mode:
authorJJ2023-08-17 23:44:19 +0000
committerJJ2023-08-17 23:44:19 +0000
commit2d5d47c89d5b9df3535029f04cf1205e47276451 (patch)
tree825accb7721d3e562967c2f680eb53be0b90e52d /docs/TYPES.md
parentd28ddab6ac5591705b4b87a408e68f14ad9e82d8 (diff)
docs: minor module changes, README example, pattern matching
Diffstat (limited to 'docs/TYPES.md')
-rw-r--r--docs/TYPES.md40
1 files changed, 34 insertions, 6 deletions
diff --git a/docs/TYPES.md b/docs/TYPES.md
index 59cfd2f..b16c508 100644
--- a/docs/TYPES.md
+++ b/docs/TYPES.md
@@ -226,27 +226,55 @@ Unions are *tagged* type unions. They provide a high-level wrapper over an inner
They are declared with `union[Variant: Type, ...]` and initialized with the name of a variant followed by its inner type constructor in brackets: `Square(side: 5)`. Tuple and struct types are special-cased to eliminate extraneous parentheses.
```puck
+type Value = uint64
type Ident = string
-type Expr = union
- Literal: Term
+type Expr = ref union
+ Literal: Value
Variable: Ident
- Abstraction: struct[param: Ident, func: Expr]
- Application: struct[func, arg: Expr]
+ Abstraction: struct[param: Ident, body: Expr]
+ Application: struct[body, arg: Expr]
Conditional: struct
- if, then, else: Expr
+ cond, then_case, else_case: Expr
```
They take up as much space in memory as the largest variant, plus the size of the tag (typically one byte).
#### pattern matching
-todo...
+Unions abstract over differing types. In order to *safely* be used, their inner types must be accessed via *pattern matching*: leaving no room for type confusion. Pattern matching in Puck relies on two syntactic constructs: the `match` statement, forcing qualification and handling of all possible types of a variable, and the `of` statement, querying type equality while simultaneously binding new identifiers to underspecified portions of variables.
```puck
+import std/tables
+
+func eval(expr: Expr, context: var HashTable[Ident, Value]): Result[Value]
+ match expr
+ of Literal(value):
+ value
+ of Variable(ident):
+ context.get(ident)
+ of Application{body, arg}:
+ if body of Abstraction{param, body as inner_body}:
+ context.set(param, eval(arg))
+ inner_body.eval(context)
+ else:
+ Error(InvalidExpr)
+ of Conditional{cond, then_case, else_case}:
+ if eval(cond, context):
+ then_case.eval(context)
+ else:
+ else_case.eval(context)
+ of _:
+ Error(InvalidExpr)
```
+The match statement takes exclusively a list of `of` sub-expressions, and checks for exhaustivity; but the `variable of Type(x)` syntax can be reused as a conditional, in `if` statements and elsewhere.
+
The `of` operator is similar to the `is` operator in that it queries type equality. However, unbound identifiers within `of` expressions are bound to appropriate values (if matched) and injected into the scope.
+When matching unions with an inner product type (structs or tuples), external extraneous parenthesis are elided.
+
+todo: guards, either `where` or `if`
+
### interfaces
Interfaces can be thought of as analogous to Rust's traits, without explicit `impl` blocks and without need for the `derive` macro.