217 lines
5.2 KiB
Markdown
217 lines
5.2 KiB
Markdown
## Arithmetic
|
|
```
|
|
1.2 * 4 + 1
|
|
# result: 5.8
|
|
```
|
|
|
|
## Few types to learn
|
|
Most important ones:
|
|
- `Num`: big-decimal
|
|
- `List[T]`: (linked-) list of `T`
|
|
- `Char`: unicode codepoint
|
|
- `Unit`: nothing
|
|
- `End`: unreachable
|
|
- `Cell[T]`: mutable cell of `T`
|
|
|
|
Advanced (uncommon) types:
|
|
- `Int8`, `Int16`, ...
|
|
- `Uint8`, ...
|
|
- `Flt32`, `Flt64`
|
|
|
|
## Functions
|
|
```
|
|
def msg(username: List[Char]) : List[Char] {
|
|
"Hello, " ++ username ++ "!" # last expr is returned
|
|
}
|
|
|
|
def main() {
|
|
print("hi")
|
|
}
|
|
main()
|
|
```
|
|
|
|
## Simple, forward type-inference
|
|
```
|
|
def zero () : Flt32 {
|
|
3.1 # error: got Num, but expected F32
|
|
}
|
|
```
|
|
|
|
## Bindings
|
|
```
|
|
let name = "Max"
|
|
let passw = "1234"
|
|
```
|
|
|
|
## Mutability only via `ref`
|
|
```
|
|
let value = Cell.from(1)
|
|
print(Num.to_str(!value)) # !cell "observes" the cell
|
|
value := 2 # `:=` mutates the cell
|
|
print(Num.to_str(!value))
|
|
```
|
|
|
|
## 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]`
|
|
- `!cell`: "observe" a mutable value
|
|
- `cell := value`: mutate a mutable value
|
|
|
|
## 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 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])
|
|
```
|
|
|
|
## automatic return types
|
|
```
|
|
def add(a: Num, b: Num) -> _ {
|
|
a + b
|
|
}
|
|
```
|
|
|
|
## templated generics
|
|
```
|
|
def [a,b] add(a: a, b: b) -> _ {
|
|
a + b
|
|
}
|
|
|
|
add(1,2)
|
|
|
|
add(1,"2") # error: in template expansion of add[Num,List[Char]]: No definition for `Num + List[Char]`
|
|
```
|
|
|
|
## pattern matching
|
|
```
|
|
type Option[t] = 'None | 'Some t
|
|
|
|
def [t] Match.`a++b`(
|
|
# matching against this value
|
|
value: List[t],
|
|
# left hand side of operator
|
|
l: List[t],
|
|
# right hand side of operator
|
|
r: MatchUtil.Var[List[t]]
|
|
) -> Option[{ r: List[t] }] {
|
|
match List.remove_prefix(l, 'from value) {
|
|
'Some rem -> 'Some { r: rem }
|
|
'None -> 'None
|
|
}
|
|
}
|
|
```
|
|
|
|
then you can do:
|
|
```
|
|
type Token = 'Public Unit | 'Private Unit | 'Err Unit;
|
|
def example(li: List[Char]) -> {t:Token,rem:List[Char]} {
|
|
match li {
|
|
"public" ++ rem -> {t: 'Public Unit, rem:rem}
|
|
"private" ++ rem -> {t: 'Private Unit, rem:rem}
|
|
_ -> {t: 'Err Unit, rem: li}
|
|
}
|
|
}
|
|
```
|
|
|
|
## recursive data types
|
|
```
|
|
type List[t] = 'End | 'Cons {head:t, tail:List[t]}
|
|
# now you might notice an issue with this
|
|
# `type` defines non-distinct type alisases
|
|
# so what is the type of this...
|
|
# Introducing: type self references
|
|
# the above example is the same as this:
|
|
type List[t] = &a ('End | 'Cons {head:t, tail:a})
|
|
|
|
# example 2:
|
|
# a List[List[t]] is just:
|
|
&b ('End | 'Cons {head: &a ('End | 'Cons {head:t, tail:a}), tail: b})
|
|
```
|
|
|
|
Infinitely sized types are not allowed:
|
|
```
|
|
&a {x:Num, y:a}
|
|
```
|
|
|
|
However, infinite types without size *are* allowed:
|
|
```
|
|
&a {x:a}
|
|
```
|
|
|
|
This is *not* allowed:
|
|
```
|
|
&a a
|
|
``` |