aboutsummaryrefslogtreecommitdiff
path: root/docs/OVERVIEW.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/OVERVIEW.md')
-rw-r--r--docs/OVERVIEW.md37
1 files changed, 18 insertions, 19 deletions
diff --git a/docs/OVERVIEW.md b/docs/OVERVIEW.md
index 1c19000..344f72e 100644
--- a/docs/OVERVIEW.md
+++ b/docs/OVERVIEW.md
@@ -22,17 +22,16 @@ Type annotations on variables and other bindings follow the name of the binding
Variables are conventionally written in `snake_case`. Types are conventionally written in `PascalCase`.
The type system is comprehensive, and complex enough to warrant delaying full coverage of until the end. Some basic types are of note, however:
- `int`, `uint`: signed and unsigned integers
- - `i8`/`i16`/`i32`/`i64`/`i128`: their fixed-size counterparts
- - `u8`/`u16`/`u32`/`u64`/`u128`: their fixed-size counterparts
+ - `i[\d+]`, `u[\d+]`: arbitrary fixed-size counterparts
- `float`, `decimal`: floating-point numbers
- `f32`/`f64`/`f128`: their fixed-size counterparts
- `dec64`/`dec128`: their fixed-size counterparts
- `byte`: an alias to `u8`, representing one byte
-- `chr`: an alias to `u32`, representing one Unicode character
+- `char`: an alias to `u32`, representing one Unicode character
- `bool`: defined as `union[false, true]`
-- `array[T, S]`: primitive fixed-size (`S`) arrays
+- `array[T, size]`: primitive fixed-size arrays
- `list[T]`: dynamic lists
-- `str`: mutable strings. internally a `list[byte]`, externally a `list[chr]`
+- `str`: mutable strings. internally a `list[byte]`, externally a `list[char]`
- `slice[T]`: borrowed "views" into the three types above
Comments are declared with `#` and run until the end of the line.
@@ -45,7 +44,7 @@ Taking cues from the Lisp family of languages, any expression may be commented o
```puck
```
-Functions are declared with the `func` keyword. They take an (optional) list of generic parameters (in brackets), an (optional) list of parameters (in parentheses), and **must** be annotated with a return type if they return a type. Every function parameter must be annotated with a type. Their type may optionally be prefixed with either `lent`, `mut` or `static`: denoting an immutable or mutable borrow (more on these later), or a *static* type (known to the compiler at compile time, and usable in `const` exprs). Generic parameters may each be optionally annotated with a type functioning as a _constraint_.
+Functions are declared with the `func` keyword. They take an (optional) list of generic parameters (in brackets), an (optional) list of parameters (in parentheses), and **must** be annotated with a return type if they return a type. Every function parameter must be annotated with a type. Their type may optionally be prefixed with either `lent`, `mut` or `const`: denoting an immutable or mutable borrow (more on these later), or a *constant* type (known to the compiler at compile time, and usable in `const` exprs). Generic parameters may each be optionally annotated with a type functioning as a _constraint_.
<!-- Functions, constants, types, and modules may be optionally prefixed with a `pub` modifier denoting visibility outside the current scope / module. More on the module system later. -->
@@ -76,18 +75,18 @@ Boolean logic and integer operations are standard and as one would expect out of
- integer division is expressed with the keyword `div` while floating point division uses `/`
- `%` is absent and replaced with distinct modulus and remainder operators
- boolean operators are bitwise and also apply to integers and floats
-- more operators are available via the standard library
+- more operators are available via the standard library (`exp` and `log`)
The above operations are performed with *operators*, special functions that take a prefixed first argument and (often) a suffixed second argument. Custom operators may be implemented, but they must consist of only a combination of the symbols `=` `+` `-` `*` `/` `<` `>` `@` `$` `~` `&` `%` `|` `!` `?` `^` `\` for the purpose of keeping the grammar context-free. They are are declared identically to functions.
-Term (in)equality is expressed with the `==` and `!=` operators. Type equality is expressed with `is`. Subtyping relations may be queried with `of`, which has the additional property of introducing new bindings in the current scope (more on this in the [types document](TYPES.md)). <!-- Membership of collections is expressed with `in`, and is overloaded for most types. -->
+Term (in)equality is expressed with the `==` and `!=` operators. Type equality is expressed with `is`. Subtyping relations may be queried with `of`, which has the additional property of introducing new bindings to the current scope in certain contexts (more on this in the [types document](TYPES.md)).
```puck
let phrase: str = "I am a string! Wheeee! ✨"
-for c in phrase:
+for c in phrase do
stdout.write(c) # I am a string! Wheeee! ✨
-for b in phrase.bytes():
- stdout.write(b.chr) # Error: cannot convert between u8 and chr
+for b in phrase.bytes() do
+ stdout.write(b.char) # Error: cannot convert from byte to char
print phrase.last() # ✨
```
@@ -98,9 +97,9 @@ String concatenation uses a distinct `&` operator rather than overloading the `+
```puck
```
-Basic conditional control flow uses standard `if`/`elif`/`else` statements. The `when` statement provides a compile-time `if`. It also takes `elif` and `else` branches and is syntactic sugar for an `if` statement within a `static` block (more on those later).
+Basic conditional control flow uses standard `if`/`elif`/`else` statements. The `when` statement provides a compile-time `if`. It also takes `elif` and `else` branches and is syntactic sugar for an `if` statement within a `const` expression (more on those later).
-All values in Puck must be handled, or explicitly discarded. This allows for conditional statements and many other control flow constructs to function as *expressions*, and evaluate to a value, when an unbound value is left at the end of each of their branches' scopes. This is particularly relevant for *functions*, where it is often idiomatic to omit an explicit `return` statement. There is no attempt made to differentiate without context, and so expressions and statements often look identical in syntax.
+All values in Puck must be handled, or explicitly discarded. This allows for conditional statements and many other control flow constructs to function as *expressions*: and evaluate to a value when an unbound value is left at the end of each of their branches' scopes. This is particularly relevant for *functions*, where it is often idiomatic to omit an explicit `return` statement. There is no attempt made to differentiate without context, and so expressions and statements often look identical in syntax.
```puck
```
@@ -118,11 +117,11 @@ type Result[T] = Result[T, ref Err]
func may_fail: Result[T] = ...
```
-Error handling is done via a fusion of functional `Option`/`Result` types and imperative `try`/`catch` statements, with much syntactic sugar. Functions may `raise` errors, but by convention should return `Option[T]` or `Result[T, E]` types instead. The compiler will note functions that `raise` errors, and force explicit qualification of them via `try`/`catch` statements.
+Error handling is done via a fusion of functional monadic types and imperative exceptions, with much syntactic sugar. Functions may `raise` exceptions, but by convention should return `Option[T]` or `Result[T, E]` types instead: these may be handled in `match` or `if`/`of` statements. The compiler will track functions that `raise` errors, and warn on those that are not handled explicitly via `try`/`with` statements.
A bevy of helper functions and macros are available for `Option`/`Result` types, and are documented and available in the `std.options` and `std.results` modules (included in the prelude by default). Two in particular are of note: the `?` macro accesses the inner value of a `Result[T, E]` or propagates (returns in context) the `Error(e)`, and the `!` accesses the inner value of an `Option[T]` / `Result[T, E]` or raises an error on `None` / the specific `Error(e)`. Both operators take one parameter and so are postfix. The `?` and `!` macros are overloaded and additionally function on types as shorthand for `Option[T]` and `Result[T]` respectively.
-The utility of the `?` macro is readily apparent to anyone who has written code in Rust or Swift. The utility of the `!` function is perhaps less so obvious. These errors raised by `!`, however, are known to the compiler: and they may be comprehensively caught by a single or sequence of `catch` statements. This allows for users used to a `try`/`catch` error handling style to do so with ease, with only the need to add one additional character to a function call.
+The utility of the `?` macro is readily apparent to anyone who has written code in Rust or Swift. The utility of the `!` function is perhaps less so obvious. These errors raised by `!`, however, are known to the compiler: and they may be comprehensively caught by a single or sequence of `catch` statements. This allows for users used to a `try`/`with` error handling style to do so with ease, with only the need to add one additional character to a function call.
More details may be found in [error handling overview](ERRORS.md).
@@ -179,9 +178,7 @@ More details may be found in the [modules document](MODULES.md).
```puck
```
-Compile-time programming may be done via the previously-mentioned `const` keyword and `when` statements: or via `const` *blocks*. All code within a `const` block is evaluated at compile-time and all assignments and allocations made are propagated to the compiled binary as static data.
-
-Further compile-time programming may be done via metaprogramming: compile-time manipulation of the abstract syntax tree. The macro system is complex, and a description may be found in the [metaprogramming document](METAPROGRAMMING.md). <!-- todo -->
+Compile-time programming may be done via the previously-mentioned `const` keyword and `when` statements: or via `const` *blocks*. All code within a `const` block is evaluated at compile-time and all assignments and allocations made are propagated to the compiled binary as static data. Further compile-time programming may be done via macros: compile-time manipulation of the abstract syntax tree. The macro system is complex, and a description may be found in the [metaprogramming document](METAPROGRAMMING.md/).
## Async System and Threading
@@ -213,7 +210,9 @@ foo( # this is usually elided
)
```
-Puck copies Rust-style ownership near verbatim. `&T` corresponds to `lent T`, `&mut T` to `mut T`, and `T` to `T`: with `T` implicitly convertible to `lent T` and `mut T` at call sites. A major goal of Puck is for all lifetimes to be inferred: there is no overt support for lifetime annotations, and it is likely code with strange lifetimes will be rejected before it can be inferred. (Total inference, however, *is* a goal.) Another major difference is the consolidation of `Box`, `Rc`, `Arc`, `Cell`, `RefCell` into just two (magic) types: `ref` and `refc`. `ref` takes the role of `Box`, and `refc` both the role of `Rc` and `Arc`: while `Cell` and `RefCell` are disregarded. The underlying motivation for compiler-izing these types is to make deeper compiler optimizations accessible: particularly with `refc`, where the existing ownership framework is used to eliminate counts. Details on memory safety, references and pointers, and deep optimizations may be found in the [memory management overview](MEMORY_MANAGEMENT.md).
+Puck copies Rust-style ownership near verbatim. `&T` corresponds to `lent T`, `&mut T` to `mut T`, and `T` to `T`: with `T` implicitly convertible to `lent T` and `mut T` at call sites. A major goal of Puck is for all lifetimes to be inferred: there is no overt support for lifetime annotations, and it is likely code with strange lifetimes will be rejected before it can be inferred. (Total inference, however, *is* a goal.)
+
+Another major difference is the consolidation of `Box`, `Rc`, `Arc`, `Cell`, `RefCell` into just two (magic) types: `ref` and `refc`. `ref` takes the role of `Box`, and `refc` both the role of `Rc` and `Arc`: while `Cell` and `RefCell` are disregarded. The underlying motivation for compiler-izing these types is to make deeper compiler optimizations accessible: particularly with `refc`, where the existing ownership framework is used to eliminate counts. Details on memory safety, references and pointers, and deep optimizations may be found in the [memory management overview](MEMORY_MANAGEMENT.md).
## Types System