From 4fba995390b9c037037ccc4bee9aeb536a9b0fde Mon Sep 17 00:00:00 2001 From: Alexander Nutz Date: Fri, 12 Sep 2025 13:59:29 +0200 Subject: [PATCH] Update spec.md --- spec.md | 92 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/spec.md b/spec.md index 01deb65..93e0229 100644 --- a/spec.md +++ b/spec.md @@ -21,15 +21,14 @@ Advanced (uncommon) types: ## Functions ``` # the type of msg is: List[Char] -> List[Char] -def msg(username: List[Char]) : List[Char] { +def msg(username: List[Char]) -> List[Char] { "Hello, " ++ username ++ "!" # last expr is returned } -# the type of main is: Unit -> Unit -def main() { +# the type of main is: Unit -> IO[Unit] +def main() -> IO[Unit] { print("hi") } -main() ``` ## Anonymus functions @@ -40,7 +39,7 @@ List.map(li, x:Num -> x * 2) ## Simple, forward type-inference ``` -def zero () : Flt32 { +def zero () -> Flt32 { 3.1 # error: got Num, but expected Flt32 } ``` @@ -48,7 +47,7 @@ def zero () : Flt32 { ## Partial function applications ``` # type of add is Num -> Num -> Num -def add(a: Num, b: Num) : Num { +def add(a: Num, b: Num) -> Num { a + b } @@ -81,20 +80,20 @@ all operators: type User = { name: List[Char] } type DbUser = { name: List[Char], pass: List[Char] } -def example4(u: User) : List[Char] { +def example4(u: User) -> List[Char] { u:name # colon is used to access fields } -def example(u: User) : DbUser { +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]} { +def example2() -> {name: List[Char], pass: List[Char]} { {name:"abc", pass:"123"} } -def example3() : User { +def example3() -> User { example2() # {name:.., pass:...} can automatically decay to {name:...} } ``` @@ -106,36 +105,36 @@ type Option[t] = | '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 { +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] { +def example2(n: Num) -> Option[Num] { 'Some n } -def example3-invalid() : Option[Num] { +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] { +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] { +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] { +def example6-error() -> Option[Unit] { let x = 'Error Unit x # in this case, the enum tag does not decay, like in `example`, @@ -150,6 +149,8 @@ def example6-error(): Option[Unit] { ### 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 @@ -194,7 +195,7 @@ def [t] Match.`a++b`( then you can do: ``` -type Token = 'Public Unit | 'Private Unit | 'Err Unit; +type Token = 'Public | 'Private | 'Err def example(li: List[Char]) -> {t:Token,rem:List[Char]} { match li { "public" ++ rem -> {t: 'Public Unit, rem:rem} @@ -322,17 +323,27 @@ def example() -> 'Int | 'UInt | 'Byte | 'Char { ## Extensible unions ``` -type Plan = dyn 'Plan; - # ^ this is not a union variant tag, but a quoted identifier +extensible union Plan -extend Plan with 'ReadlnPlan -extend Plan with 'WritelnPlan +extend Plan with 'ReadlnPlan Unit +extend Plan with 'WritelnPlan Unit # pattern matching against these is always non-exhaustive. -# can only pattern match with the imported extensions - +# can only pattern match with the imported extensions ``` +## Any type +in the stdlib: +``` +extensible union Any + +type Any.LambdaCalc = 'Apply {fn: Any.LambdaCalc, arg: Any.LambdaCalc} + | 'Scope {idx: Uint} + | 'Abstr {inner: Any.LambdaCalc} +def Any.toLambda(a: Any) -> Any.LambdaCalc +``` +It gets automatically extended with every type ever used. + ## Lenses In the stdlib: ``` @@ -365,6 +376,39 @@ myHeader with header:text: "new meta:header:text value" ## Pure IO +Something like this is done in the stdlib: ``` -TODO +extensible union IO.Plan[r] +type IO[t] = 'Just {value: t} + | 'More template r, f: {plan: IO.Plan[r], then: r -> IO[f], finally: f -> t} + +def [a,b] `await a (a->b)`(io: IO[a], then: a -> b) -> IO[b] { + match io { + 'Just {value} -> 'Just {value: then(value)}, + 'More &a {finally,...} -> a with finally: r -> then(finally(r)) + } +} + +# in stdio: +extend IO.Plan[Uint8] with 'stdio.ReadByte {stream: Int32} +def stdio.getchar : IO[Uint8] = 'More {plan: 'stdio.ReadByte {stream: 0}, then: by -> 'Just by[0], finally: x->x} + +def main() -> IO[Unit] +``` + +the runtime does something like this: +``` +def [a] RUNTIME_EVAL(io: IO[a]) -> a { + match io { + 'Just {value} -> value + 'More {plan, then, finally} -> finally(RUNTIME_EVAL(then(match plan { + 'ReadStream {stream} -> 'ReadStreamIOResult {data: impure perform the io here lol} + _ -> impure error here "this runtime doesn't support this kind of IO" or sth + }))) + } +} + +def RUNTIME_ENTRY() { + RUNTIME_EVAL ( main() ) +} ``` \ No newline at end of file