diff --git a/spec.md b/spec.md new file mode 100644 index 0000000..c1dfaed --- /dev/null +++ b/spec.md @@ -0,0 +1,168 @@ +## 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)) +``` + +## (Almost) no 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:...} *) +} +``` + +## mixed-tagged union types +``` +type Option[t] = + 'Err Unit (* Unit that has tag enum 'Err *) +| t (* has no tag *) + +(* 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] { + n (* this works, because the `t` is not tagged in option *) +} + +def example3-invalid() : Option[Num] { + Unit (* error: can't convert type `Unit` into type `'Err Unit | t` + * Either label the expression with 'Err, + * or change the return type to Option[Unit] *) +} + +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 | Unit` into type `'Err Unit | Num` + * The case `Unit` does not exist in the target `'Err Unit | 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 | 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 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} + } +} +``` + +## pattern 1: labelled arguments +``` +def [t] List.remove_prefix(prefix: List[t], list: 'from List[t]) +``` \ No newline at end of file