aboutsummaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'README.md')
-rw-r--r--README.md76
1 files changed, 58 insertions, 18 deletions
diff --git a/README.md b/README.md
index 6703bcb..d3e9757 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
A place where I can make some bad decisions.
Puck is an experimental, memory safe, structurally typed, interface-first, imperative programming language.
-It aims to be clean and succinct while performant: inspired by the syntax and metaprogramming of [Nim](https://nim-lang.org/), the error handling of [Swift](https://www.swift.org/), the performance and safety guarantees of [Rust](https://www.rust-lang.org/), the async/await and comptime of [Zig](https://ziglang.org/), and the module system of [OCaml](https://ocaml.org/).
+It aims to be consistent and succinct while performant: inspired by the syntax and metaprogramming of [Nim](https://nim-lang.org/), the error handling of [Swift](https://www.swift.org/), the memory management of [Rust](https://www.rust-lang.org/) and [Koka](https://koka-lang.github.io/), the async/await and comptime of [Zig](https://ziglang.org/), and the module system of [OCaml](https://ocaml.org/).
<details>
<summary><b>Example: Type Classes</b></summary>
@@ -22,17 +22,37 @@ pub type Err = class
dbg(Self): str
## A Result type that uses dynamically dispatched errors.
-## The Error may be any type implementing Err.
+## The Error may be any type implementing the Err class.
pub type Result[T] = Result[T, ref Err]
-## Implements the dbg function for strings.
-## As the str function is already defined for strings,
+## Implements the `dbg` function for strings.
+## As the `str` function is already defined for strings,
## this in turn means strings now implicitly implement Err.
pub func dbg(self: str) = "\"" & self & "\""
```
</details>
+<details>
+<summary><b>Example: Metaprogramming</b></summary>
+
+```nim
+# Note: These declarations are adapted from the standard prelude.
+
+## Syntactic sugar for dynamic result type declarations.
+pub macro !(T: type) =
+ quote Result[`T`]
+
+## Indirect access. Propagates `Error`.
+pub macro ?[T, E](self: Result[T, E]) =
+ quote
+ match `self`
+ of Okay(x) then x
+ of Error(e) then return Error(e)
+```
+
+</details>
+
<details open>
<summary><b>Example: Pattern Matching</b></summary>
@@ -40,9 +60,9 @@ pub func dbg(self: str) = "\"" & self & "\""
## Opens the std.tables module for unqualified use.
use std.tables
-pub type Value = string
-pub type Ident = string
-pub type Expr = ref union
+pub type Value = str
+pub type Ident = str
+pub type Expr = ref union # tagged, algebraic unions
Literal(Value)
Variable(Ident)
Abstraction(param: Ident, body: Expr)
@@ -51,19 +71,20 @@ pub type Expr = ref union
then_branch: Expr, else_branch: Expr)
## Evaluate an Expr down to a Value, or return an Error.
-pub func eval(context: mut HashTable[Ident, Value], expr: Expr): Result[Value] =
- match expr
- of Literal(value) then Okay(value)
+pub func eval(context: mut Table[Ident, Value], expr: lent Expr): Value! =
+ match expr # structural pattern matching and guards are supported but not shown
+ of Literal(value) then
+ Okay(value.clone) # ownership necessitates we explicitly clone
of Variable(ident) then
- context.get(ident)
+ context.get(ident) # whitespace is significant but flexible
.err("Could not find variable {} in context!"
- .fmt(ident))
+ .fmt(ident)) # uniform function call syntax allows arbitrary piping/chaining
of Application(body, arg) then
- if body of Abstraction(param, body as inner_body) then
+ if body of Abstraction(param, body as inner_body) then # compact matching with if
context.set(param, context.clone.eval(arg)?)
- context.eval(inner_body)
+ context.eval(inner_body) # all values must be handled: returns are implicit
else
- Error("Expected Abstraction, found body {} and argument {}".fmt(body, arg))
+ Error("Expected Abstraction, found body {} and arg {}".fmt(body.clone, arg.clone))
of Conditional(condition, then_branch, else_branch) then
if context.clone.eval(condition)? == "true" then
context.eval(then_case)
@@ -78,7 +99,26 @@ pub func eval(context: mut HashTable[Ident, Value], expr: Expr): Result[Value] =
<summary><b>Example: Modules</b></summary>
```nim
-...
+# The top-level module declaration can be elided if the file shares the same name.
+pub mod tables =
+ ## The Table class. Any sort of table - no matter the underlying
+ ## representation - must implement these methods.
+ pub type Table[K, V] = class
+ get(lent Self, lent K): lent V?
+ get(mut Self, lent K): mut V?
+ set(mut Self, lent K, V): V?
+ pop(mut Self, lent K): V?
+ clear(mut Self)
+ size(lent Self): uint
+ init(varargs (K, V)): Self
+
+ ...
+
+ pub mod hashtable =
+ use std.hashes
+
+ pub type HashTable[K, V] = struct
+ ...
```
</details>
@@ -90,8 +130,8 @@ Don't use it. Everything is unimplemented and it will break underneath your feet
That said: in the future, once somewhat stabilized, reasons why you *would* use it would be for:
- The **syntax**, aiming to be flexible, predictable, and succinct, through the use of *uniform function call syntax* and significant whitespace
-- The **type system**, being modern and powerful with a strong emphasis on safety, optional and result types, algebraic data types, interfaces, and modules
-- The **memory management system**, implementing a model of strict ownership while allowing individual fallbacks to reference counts if so desired
+- The **type system**, being modern and powerful with a strong emphasis on safety, algebraic data types, optional and result types, first-class functions, generics, interfaces, and modules
+- The **memory management system**, implementing a model of strict ownership with an optimized reference counting escape hatch
- The **metaprogramming**, providing integrated macros capable of rewriting the abstract syntax tree before or after typechecking
- The **interop system**, allowing foreign functions to be usable with native semantics from a bevy of languages