new spec done soon

This commit is contained in:
2025-09-15 16:15:23 +02:00
parent 6079d2ece1
commit 74075b4612

255
spec.md
View File

@@ -73,12 +73,44 @@ TODO ebnf
## Compatible-with ## Compatible-with
TODO Check if type is compatible-with / assignable-to type requirements.
- a type is compatible with itself ofcourse
- a tagged union is compatible with anothr,
if the other one contains at least all cases from this union,
and those cases are compatible
- a non-nominal record is compatbile with anohter,
if the other one contains a _subset_ of the fields of this one,
and the field types are comptible.
- a type is compatible with a partial type `?Ty`,
if the definition of the partial type is in the current scope,
and the type is compatible with the type inside the definition
- partial records and unions are similar to the above.
TODO: need more details?
## Phi unification ## Phi unification
TODO This process is performed on the resulting types of two merging branches
Tries, in order:
- if one of the types is a tagged union,
which contains only one case with an identical type,
and the other is not a tagged union,
it will "attach" that case onto the other type, like this:
`'N Num | 'I Int` and `Num` -> `'N Num | 'I Int`
- if one of the types is a tagged union with exactly one case,
and the other one is not an union,
it will put that tag onto it, and phi-merge the inner types.
example: `'N Num` and `Num` -> `'N Num`
- if both types are tagged unions,
the result is an tagged union contain the csaes of both tagged unions.
union cases that are in both branches, will have their types phi-unified too.
example: `'Ok ('N Num | 'I Int) | 'SomeErr` and `'Ok Num | 'AnotherErr` -> `'Ok ('N Num) | 'SomeErr | 'AnotherErr`
- if both types are non-nominal records,
the result will contain only fields that are present in both types.
The types of the fields will get phi-unified too
example: `{a:Num,b:Num,c:Num}` and `{a:Num,c:Num,e:Num}` -> `{a:Num, e:Num}`
# "Platform library" # "Platform library"
@@ -96,7 +128,6 @@ type Uint8
def Uint8.wrapping_add : Uint8 -> Uint8 -> Uint8 def Uint8.wrapping_add : Uint8 -> Uint8 -> Uint8
def Uint8.bitwise_not : Uint8 -> Uint8 def Uint8.bitwise_not : Uint8 -> Uint8
def Uint8.less_than : Uint8 -> Uint8 -> Bool def Uint8.less_than : Uint8 -> Uint8 -> Bool
def Uint8.equal : Uint8 -> Uint8 -> Bool
def Uint8.bits : Uint8 -> {7:Bool,6:Bool,5:Bool,4:Bool,3:Bool,2:Bool,1:Bool,0:Bool} def Uint8.bits : Uint8 -> {7:Bool,6:Bool,5:Bool,4:Bool,3:Bool,2:Bool,1:Bool,0:Bool}
``` ```
@@ -137,6 +168,36 @@ def Bool.xor : Bool -> Bool -> Bool
## arbitrary-precision signed decimal number ## arbitrary-precision signed decimal number
type Num type Num
def Num.toStr : Num -> Char List
# consider using locale aware, and space ignoring parsing instead
# parses of format 123 / -123 / 123.456
def Num.parseLit : Char List -> Num Option
def Num.zero : Num
def Num.one : Num
def `a+b` : Num -> Num -> Num
def `a-b` : Num -> Num -> Num
def `a*b` : Num -> Num -> Num
def `a/b` : Num -> Num -> Num
def Num.neg : Num -> Num
############ Char ############
# one unicode codepoint
type Char
def Char.toAscii : Char -> Uint8 Option
def Char.fromAscii : Uint8 -> Char Option
def Char.encodeUtf8 : Char -> Uint8 List
def Char.decodeUtf8 : Uint8 List -> 'Cons {v: Char, next: Uint8 List}
############ String ############
def String.encodeUtf8 : Char List -> Utf8 List
def String.decodeUtf8 : Uint8 List -> 'Ok Char List | 'Err {at: Num}
############ t List ############ ############ t List ############
## A generic linked-list type ## A generic linked-list type
@@ -224,140 +285,80 @@ TODO
TODO: add either attribute system, or comptime exec TODO: add either attribute system, or comptime exec
# Mostly forward type inference
Exception 1:
```
def List.map : t List -> (t -> r) -> r List
# doesn't require specifying types in lambda: simple one-step backwards type inference
List.map([1,2,3], x -> x * 2)
```
Exception 2:
```
def add : Num -> Num -> Num =
a -> b -> a + b
# ^^^^^^
# does not require specification of those types, because already specified in function signature
# this also applies to:
def add : Num -> Num -> Num
def add = a -> b -> a + b
```
# Expressions # Expressions
- `expr:field` access field from non-nominal record
- `let varName = value exprUsingTheVar`
- `'a'` unicode codepoint literal, aka "char" literal. returns a `Char`
- `[1,2,3]` list literal: creates a `t List`, where `t` is equal to the type of all the wrapped expressions
- `"Hello\\nworld"` string literal with escape characters. same behaviour as list literal of the chars
- `12.345`, or `12`, or `-12`: same behaviour as `Num.parseLit` on the value (as string)
- `arg -> value`: one-step backwards inferrable lambda
- `arg: Type -> value`
- `func(arg1, arg2)`: function application. requires at least one argument. partial function applications are allowed too
- `expr :: type`: down-cast type
- `recExpr with fieldname: newFieldValue`: overwrites or adds a field to a record type.
type checking: identical to `recExpr and {fieldname: newFieldValue}`
- `recExpr and otherRecExpr`: "sum" fields together of both record expressions.
type checking: phi-unify `recExpr` with `otherRecExpr`, and require that both are non-nominal record types
- `if cond then a else b`
- `{field1: val1, field2: val2}` field construction
- `match expr with <match cases>`: [pattern matching](##pattern-matching)
- `a = b` the [equality operator](##equality-operator)
- `a + b`: identical to `\`a+b\`(a, b)` name: "sum"
- `a - b`: identical to `\`a-b\`(a, b)` name: "difference"
- `a * b`: identical to `\`a*b\`(a, b)` name: "times"
- `a / b`: identical to `\`a/b\`(a, b)` name: "over"
- `a ++ b`: identical to `\`a++b\`(a,b)` name: "list concatenate"
- `a => b`: identical to `\`a=>b\`(a,b)` name: "lens compose"
## pattern matching
TODO TODO
## equality operator
The only operator with type overloading
TODO
# coding patterns (for users only)
## labelled arguments
```
def List.remove_prefix(prefix: t List, list: 'from t List)
List.remove_prefix([1,2], 'from [1,2,3,4])
# but this also works most of the times:
List.remove_prefix([1,2], [1,2,3,4])
```
# OLD SPECIFICATION STARTING HERE # OLD SPECIFICATION STARTING HERE
## Anonymus functions
```
The type of List.map is List[t] -> (t -> t) -> List[t]
List.map(li, x:Num -> x * 2)
```
## Simple, forward type-inference
```
def zero () -> Flt32 {
3.1 # error: got Num, but expected Flt32
}
```
## Partial function applications
```
# type of add is Num -> Num -> Num
def add(a: Num, b: Num) -> Num {
a + b
}
let a = add(1) # type of a is Num -> Num
let b = a(2) # type of b is Num
# b is 3
```
## Bindings
```
let name = "Max"
let passw = "1234"
```
## no confusing function or operator overloading
all operators:
- `Num + Num` (has overloads for fixed width number types)
- `Num - Num` (has overloads for fixed width number types)
- `Num * Num` (has overloads for fixed width number types)
- `Num / Num` (has overloads for fixed width number types)
- `Num ^ Num`: raise to the power (has overloads for fixed width number types)
- `List[t] ++ List[t]`: list concatenation
- `value :: t` (explicitly specify type of value, useful for down-casting structs, or just code readability; does not perform casting)
- `list[index]`
- `a => b`: lens compose
## non-nominal struct types
```
# `type` creates a non-distinct type alias
type User = { name: List[Char] }
type DbUser = { name: List[Char], pass: List[Char] }
def example4(u: User) -> List[Char] {
u:name # colon is used to access fields
}
def example(u: User) -> DbUser {
u with pass: "1234"
# has type { name: List[Char], pass: List[Char] }
}
def example2() -> {name: List[Char], pass: List[Char]} {
{name:"abc", pass:"123"}
}
def example3() -> User {
example2() # {name:.., pass:...} can automatically decay to {name:...}
}
```
## (tagged) union types
```
type Option[t] =
'Err # If no type specified after tag, defaults to Unit
| 'Some t
# the tags of unions are weakly attached to the types, but won't decay unless they have to
def example(n: Num) -> Num {
let x = 'MyTag n # type of x is 'MyTag Num
x # tag gets removed because target type is Num
}
def example2(n: Num) -> Option[Num] {
'Some n
}
def example3-invalid() -> Option[Num] {
Unit # error: can't convert type `Unit` into type `'Err Unit | 'Some Num`
# Either label the expression with 'Err,
# or change the return type to Option[Unit], and label the expression with 'Some
}
def exampe4() -> Option[Num] {
'Err Unit
# type of this expression is: `'Err Unit`
# enums can automatically cast, if all the cases from the source enum also exists in the target enum,
# which they do here: `'Err Unit` is a case in `'Err Unit | Num`
}
def example5-error() -> Option[Num] {
let x = ( 'Err Unit ) :: Option[Unit]
x
# error: can't convert type `'Err Unit | 'Some Unit` into type `'Err Unit | 'Some Num`
# The case `'Some Unit` does not exist in the target `'Err Unit | 'Some Num`
}
def example6-error() -> Option[Unit] {
let x = 'Error Unit
x
# in this case, the enum tag does not decay, like in `example`,
# because we are casting to an enum
# error: can't convert type `'Error Unit` into type `'Err Unit | 'Some Num``
# 1st possible solution: manually cast to just `Unit` (via `expr :: Unit`), so that it can convert to the second case of the target
# 2nd possible solution: pattern match against the enum, to rename the tag from 'Error to 'Err
}
```
### pattern 1: labelled arguments
```
def [t] List.remove_prefix(prefix: List[t], list: 'from List[t])
List.remove_prefix([1,2], 'from [1,2,3,4])
```
## automatic return types ## automatic return types
``` ```
def add(a: Num, b: Num) -> _ { def add(a: Num, b: Num) -> _ {