diff options
author | JJ | 2024-05-20 22:38:13 +0000 |
---|---|---|
committer | JJ | 2024-05-20 23:06:12 +0000 |
commit | f2c5b00c13c698414e82b77f076447337e9cbac0 (patch) | |
tree | 1e5e0c63d8034b5d79943973ea2169c6b5a408c8 /docs/book/OVERVIEW.html | |
parent | 9bbf69870ff0e857156ae2cfa36915cb21f90938 (diff) |
Diffstat (limited to 'docs/book/OVERVIEW.html')
-rw-r--r-- | docs/book/OVERVIEW.html | 72 |
1 files changed, 52 insertions, 20 deletions
diff --git a/docs/book/OVERVIEW.html b/docs/book/OVERVIEW.html index 1a4459f..f96acff 100644 --- a/docs/book/OVERVIEW.html +++ b/docs/book/OVERVIEW.html @@ -88,7 +88,7 @@ <nav id="sidebar" class="sidebar" aria-label="Table of contents"> <div class="sidebar-scrollbox"> - <ol class="chapter"><li class="chapter-item expanded affix "><a href="../index.html">The Puck Programming Language</a></li><li class="chapter-item expanded "><a href="OVERVIEW.html" class="active"><strong aria-hidden="true">1.</strong> Basic Usage</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">1.1.</strong> Variables and Comments</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.2.</strong> Basic Types</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.3.</strong> Functions and Calls</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.4.</strong> Boolean and Integer Operations</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.5.</strong> Conditionals and Control Flow</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.6.</strong> Error Handling</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.7.</strong> Loops and Iterators</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.8.</strong> Modules</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.9.</strong> Compile-time Programming</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.10.</strong> Async and Threading</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.11.</strong> Advanced Types</div></li></ol></li><li class="chapter-item expanded "><a href="SYNTAX.html"><strong aria-hidden="true">2.</strong> Syntax</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">2.1.</strong> Indentation Rules [todo]</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">2.2.</strong> Reserved Keywords</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">2.3.</strong> A Formal Grammar</div></li></ol></li><li class="chapter-item expanded "><a href="TYPES.html"><strong aria-hidden="true">3.</strong> Type System</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">3.1.</strong> Basic Types</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">3.2.</strong> Parameter Types</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">3.3.</strong> Reference Types</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">3.4.</strong> Abstract Types</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">3.5.</strong> Advanced Types</div></li></ol></li><li class="chapter-item expanded "><a href="MODULES.html"><strong aria-hidden="true">4.</strong> Module System</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">4.1.</strong> Using Modules</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">4.2.</strong> Implicit Modules</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">4.3.</strong> Defining Module Interfaces [todo]</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">4.4.</strong> Defining an External API [todo]</div></li></ol></li><li class="chapter-item expanded "><a href="ERRORS.html"><strong aria-hidden="true">5.</strong> Error Handling</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">5.1.</strong> Errors as Monads</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">5.2.</strong> Errors as Catchable Exceptions</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">5.3.</strong> Errors and Void Functions [todo]</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">5.4.</strong> Unrecoverable Exceptions</div></li></ol></li><li class="chapter-item expanded "><a href="ASYNC.html"><strong aria-hidden="true">6.</strong> Async System</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">6.1.</strong> Threading [todo]</div></li></ol></li><li class="chapter-item expanded "><a href="METAPROGRAMMING.html"><strong aria-hidden="true">7.</strong> Metaprogramming</a></li><li class="chapter-item expanded "><div><strong aria-hidden="true">8.</strong> Memory Management [todo]</div></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">8.1.</strong> Reference Counting Optimizations</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">8.2.</strong> Annotations and Ownership</div></li></ol></li><li class="chapter-item expanded "><a href="INTEROP.html"><strong aria-hidden="true">9.</strong> Language Interop [draft]</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">9.1.</strong> Rust, Swift, Nim</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">9.2.</strong> Java, Kotlin</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">9.3.</strong> Python, Racket, C</div></li></ol></li><li class="chapter-item expanded "><div><strong aria-hidden="true">10.</strong> Refinement Types [draft]</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">11.</strong> Dependent Types [draft]</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">12.</strong> Effects System [draft]</div></li></ol> + <ol class="chapter"><li class="chapter-item expanded affix "><a href="../index.html">The Puck Programming Language</a></li><li class="chapter-item expanded "><a href="OVERVIEW.html" class="active"><strong aria-hidden="true">1.</strong> Basic Usage</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">1.1.</strong> Variables and Comments</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.2.</strong> Functions and Indentation</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.3.</strong> Uniform Function Call Syntax</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.4.</strong> Basic Types</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.5.</strong> Conditionals and Pattern Matching</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.6.</strong> Error Handling</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.7.</strong> Blocks and Loops</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.8.</strong> Module System</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.9.</strong> Compile-time Programming</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.10.</strong> Async System and Threading</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.11.</strong> Memory Management</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.12.</strong> Types System</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.13.</strong> Structs and Tuples</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.14.</strong> Unions and Enums</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">1.15.</strong> Classes</div></li></ol></li><li class="chapter-item expanded "><a href="SYNTAX.html"><strong aria-hidden="true">2.</strong> Syntax</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">2.1.</strong> Call Syntax</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">2.2.</strong> Indentation Rules</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">2.3.</strong> Expression Rules</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">2.4.</strong> Reserved Keywords</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">2.5.</strong> A Formal Grammar</div></li></ol></li><li class="chapter-item expanded "><a href="TYPES.html"><strong aria-hidden="true">3.</strong> Type System</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">3.1.</strong> Basic Types</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">3.2.</strong> Parameter Types</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">3.3.</strong> Reference Types</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">3.4.</strong> Abstract Types</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">3.5.</strong> Advanced Types</div></li></ol></li><li class="chapter-item expanded "><a href="MODULES.html"><strong aria-hidden="true">4.</strong> Module System</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">4.1.</strong> What are modules?</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">4.2.</strong> Using modules</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">4.3.</strong> Implicit modules</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">4.4.</strong> Defining interfaces [todo]</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">4.5.</strong> Defining an external API [todo]</div></li></ol></li><li class="chapter-item expanded "><a href="MEMORY_MANAGEMENT.html"><strong aria-hidden="true">5.</strong> Memory Management [todo]</a></li><li class="chapter-item expanded "><a href="METAPROGRAMMING.html"><strong aria-hidden="true">6.</strong> Metaprogramming</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">6.1.</strong> Scope</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">6.2.</strong> Usage</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">6.3.</strong> Quoting [todo]</div></li></ol></li><li class="chapter-item expanded "><a href="ERRORS.html"><strong aria-hidden="true">7.</strong> Error Handling</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">7.1.</strong> Errors as monads</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">7.2.</strong> Errors as checked exceptions</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">7.3.</strong> Errors as effects [todo]</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">7.4.</strong> Unrecoverable Exceptions</div></li></ol></li><li class="chapter-item expanded "><a href="ASYNC.html"><strong aria-hidden="true">8.</strong> Async System</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">8.1.</strong> Effects System [todo]</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">8.2.</strong> Threading [todo]</div></li></ol></li><li class="chapter-item expanded "><a href="INTEROP.html"><strong aria-hidden="true">9.</strong> Language Interop [draft]</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">9.1.</strong> Rust</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">9.2.</strong> Swift, Nim</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">9.3.</strong> Java, Kotlin</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">9.4.</strong> Python, Racket, C</div></li></ol></li><li class="chapter-item expanded "><div><strong aria-hidden="true">10.</strong> Effects System [draft]</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">11.</strong> Refinement Types [draft]</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">12.</strong> Dependent Types [draft]</div></li><li class="chapter-item expanded "><a href="EXAMPLES.html"><strong aria-hidden="true">13.</strong> Examples</a></li></ol> </div> <div id="sidebar-resize-handle" class="sidebar-resize-handle"> <div class="sidebar-resize-indicator"></div> @@ -178,11 +178,11 @@ <p>It attempts to explore designs in making functional programming paradigms comfortable to those familiar with imperative and object-oriented languages, as well as deal with some more technical problems along the way, such as integrated refinement types and typesafe interop.</p> <p>This is the language I keep in my head. It reflects the way I think and reason about code.</p> <p>I do hope others enjoy it.</p> -<h2 id="declarations-and-comments"><a class="header" href="#declarations-and-comments">Declarations and Comments</a></h2> +<h2 id="variables-and-comments"><a class="header" href="#variables-and-comments">Variables and Comments</a></h2> <pre><code class="language-puck">let ident: int = 413 # type annotations are optional var phrase = "Hello, world!" -const compile_time = when linux then "linux" else "windows" +const compile_time = std.os.file_name </code></pre> <p>Variables may be mutable (<code>var</code>), immutable (<code>let</code>), or compile-time evaluated and immutable (<code>const</code>). Type annotations on variables and other bindings follow the name of the binding (with <code>: Type</code>), and are typically optional. @@ -191,7 +191,7 @@ The type system is comprehensive, and complex enough to warrant delaying full co <ul> <li><code>int</code>, <code>uint</code>: signed and unsigned integers <ul> -<li><code>i[\d+]</code>, <code>u[\d+]</code>: arbitrary fixed-size counterparts</li> +<li><code>i[\d]+</code>, <code>u[\d]+</code>: arbitrary fixed-size counterparts</li> </ul> </li> <li><code>float</code>, <code>decimal</code>: floating-point numbers @@ -226,7 +226,7 @@ print [1, 2, 3].inc(1) # 2, 3, 4 print [1].len # 1 </code></pre> <p>Puck supports <em>uniform function call syntax</em>: and so any function may be called using the typical syntax for method calls, that is, the first parameter of any function may be appended with a <code>.</code> and moved to precede it, in the style of a typical method. (There are no methods in Puck. All functions are statically dispatched. This may change in the future.)</p> -<p>This allows for a number of syntactic cleanups. Arbitrary functions with compatible types may be chained with no need for a special pipe operator. Object field access, module member access, and function calls are unified, reducing the need for getters and setters. Given a first type, IDEs using dot-autocomplete can fill in all the functions defined for that type. Programmers from object-oriented languages may find the lack of classes more bearable. UFCS is implemented in shockingly few languages, and so Puck joins the tiny club that previously consisted of just D and Nim.</p> +<p>This allows for a number of syntactic cleanups. Arbitrary functions with compatible types may be chained with no need for a special pipe operator. Object field access, module member access, and function calls are unified, reducing the need for getters and setters. Given a first type, IDEs using dot-autocomplete can fill in all the functions defined for that type. Programmers from object-oriented languages may find the lack of object-oriented classes more bearable. UFCS is implemented in shockingly few languages, and so Puck joins the tiny club that previously consisted of just D, Nim, Koka, and Effekt.</p> <h2 id="basic-types"><a class="header" href="#basic-types">Basic Types</a></h2> <pre><code class="language-puck"></code></pre> <p>Boolean logic and integer operations are standard and as one would expect out of a typed language: <code>and</code>, <code>or</code>, <code>xor</code>, <code>not</code>, <code>shl</code>, <code>shr</code>, <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, <code><</code>, <code>></code>, <code><=</code>, <code>>=</code>, <code>div</code>, <code>mod</code>, <code>rem</code>. Notably:</p> @@ -235,7 +235,7 @@ print [1].len # 1 <li>integer division is expressed with the keyword <code>div</code> while floating point division uses <code>/</code></li> <li><code>%</code> is absent and replaced with distinct modulus and remainder operators</li> <li>boolean operators are bitwise and also apply to integers and floats</li> -<li>more operators are available via the standard library (<code>exp</code> and <code>log</code>)</li> +<li>more operators are available via the standard library (<code>std.math.exp</code> and <code>std.math.log</code>)</li> </ul> <p>The above operations are performed with <em>operators</em>, 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 <code>=</code> <code>+</code> <code>-</code> <code>*</code> <code>/</code> <code><</code> <code>></code> <code>@</code> <code>$</code> <code>~</code> <code>&</code> <code>%</code> <code>|</code> <code>!</code> <code>?</code> <code>^</code> <code>\</code> for the purpose of keeping the grammar context-free. They are are declared identically to functions.</p> <p>Term (in)equality is expressed with the <code>==</code> and <code>!=</code> operators. Type equality is expressed with <code>is</code>. Subtyping relations may be queried with <code>of</code>, which has the additional property of introducing new bindings to the current scope in certain contexts (more on this in the <a href="TYPES.html">types document</a>).</p> @@ -259,9 +259,9 @@ print phrase.last() # ✨ <pre><code class="language-puck">type Result[T] = Result[T, ref Err] func may_fail: Result[T] = ... </code></pre> -<p>Error handling is done via a fusion of functional monadic types and imperative exceptions, with much syntactic sugar. Functions may <code>raise</code> exceptions, but by convention should return <code>Option[T]</code> or <code>Result[T, E]</code> types instead: these may be handled in <code>match</code> or <code>if</code>/<code>of</code> statements. The compiler will track functions that <code>raise</code> errors, and warn on those that are not handled explicitly via <code>try</code>/<code>with</code> statements.</p> +<p>Error handling is done via a fusion of functional monadic types and imperative exceptions, with much syntactic sugar. Functions may <code>raise</code> exceptions, but by convention should return <code>Option[T]</code> or <code>Result[T, E]</code> types instead: these may be handled in <code>match</code> or <code>if</code>/<code>of</code> statements. The effect system built into the compiler will track functions that <code>raise</code> errors, and warn on those that are not handled explicitly via <code>try</code>/<code>with</code> statements anywhere on the call stack.</p> <p>A bevy of helper functions and macros are available for <code>Option</code>/<code>Result</code> types, and are documented and available in the <code>std.options</code> and <code>std.results</code> modules (included in the prelude by default). Two in particular are of note: the <code>?</code> macro accesses the inner value of a <code>Result[T, E]</code> or propagates (returns in context) the <code>Error(e)</code>, and the <code>!</code> accesses the inner value of an <code>Option[T]</code> / <code>Result[T, E]</code> or raises an error on <code>None</code> / the specific <code>Error(e)</code>. Both operators take one parameter and so are postfix. The <code>?</code> and <code>!</code> macros are overloaded and additionally function on types as shorthand for <code>Option[T]</code> and <code>Result[T]</code> respectively.</p> -<p>The utility of the <code>?</code> macro is readily apparent to anyone who has written code in Rust or Swift. The utility of the <code>!</code> function is perhaps less so obvious. These errors raised by <code>!</code>, however, are known to the compiler: and they may be comprehensively caught by a single or sequence of <code>catch</code> statements. This allows for users used to a <code>try</code>/<code>with</code> error handling style to do so with ease, with only the need to add one additional character to a function call.</p> +<p>The utility of the <code>?</code> macro is readily apparent to anyone who has written code in Rust or Swift. The utility of the <code>!</code> function is perhaps less so obvious. These errors raised by <code>!</code>, however, are known to the compiler: and they may be comprehensively caught by a single or sequence of <code>with</code> statements. This allows for users used to a <code>try</code>/<code>with</code> error handling style to do so with ease, with only the need to add one additional character to a function call.</p> <p>More details may be found in <a href="ERRORS.html">error handling overview</a>.</p> <h2 id="blocks-and-loops"><a class="header" href="#blocks-and-loops">Blocks and Loops</a></h2> <pre><code class="language-puck">loop @@ -273,12 +273,12 @@ for i in 0 .. 3 do # exclusive print "{} {}".fmt(i, j) </code></pre> <p>Three types of loops are available: <code>for</code> loops, <code>while</code> loops, and infinite loops (<code>loop</code> loops). For loops take a binding (which may be structural, see pattern matching) and an iterable object and will loop until the iterable object is spent. While loops take a condition that is executed upon the beginning of each iteration to determine whether to keep looping. Infinite loops are infinite are infinite are infinite are infinite are infinite are infinite and must be manually broken out of.</p> -<p>There is no special concept of iterators: iterable objects are any object that implements the <code>Iter[T]</code> class (more on those in <a href="TYPES.html">the type system document</a>), that is, provides a <code>self.next()</code> function returning an <code>Option[T]</code>. As such, iterators are first-class constructs. For loops can be thought of as while loops that unwrap the result of the <code>next()</code> function and end iteration upon a <code>None</code> value. While loops, in turn, can be thought of as infinite loops with an explicit conditional break.</p> +<p>There is no special concept of iterators: iterable objects are any object that implements the <code>Iter[T]</code> class (more on those in <a href="TYPES.html">the type system document</a>): that is, provides a <code>self.next()</code> function returning an <code>Option[T]</code>. As such, iterators are first-class constructs. For loops can be thought of as while loops that unwrap the result of the <code>next()</code> function and end iteration upon a <code>None</code> value. While loops, in turn, can be thought of as infinite loops with an explicit conditional break.</p> <p>The <code>break</code> keyword immediately breaks out of the current loop, and the <code>continue</code> keyword immediately jumps to the next iteration of the current loop. Loops may be used in conjunction with blocks for more fine-grained control flow manipulation.</p> <pre><code class="language-puck">block statement -let x = block: +let x = block let y = read_input() transform_input(y) @@ -296,14 +296,34 @@ block foo <p>Within a module, functions, types, constants, and other modules may be <em>exported</em> for use by other modules with the <code>pub</code> keyword. All such identifiers are private by default and only accessible module-locally without. Modules are first-class and may be bound, inspected, modified, and returned. As such, imported modules may be <em>re-exported</em> for use by other modules by binding them to a public constant.</p> <p>More details may be found in the <a href="MODULES.html">modules document</a>.</p> <h2 id="compile-time-programming"><a class="header" href="#compile-time-programming">Compile-time Programming</a></h2> -<pre><code class="language-puck"></code></pre> -<p>Compile-time programming may be done via the previously-mentioned <code>const</code> keyword and <code>when</code> statements: or via <code>const</code> <em>blocks</em>. All code within a <code>const</code> 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 <a href="METAPROGRAMMING.html">metaprogramming document</a>.</p> +<pre><code class="language-puck">## Arbitrary code may execute at compile-time. +const compile_time = + match std.os.platform # known at compile-time + of Windows then "windows" + of MacOS then "darwin" + of Linux then "linux" + of Wasi then "wasm" + of _ then "unknown platform" + +## The propagation operator is a macro so that `return` is injected into the function scope. +pub macro ?[T](self: Option[T]) = + quote + match `self` + of Some(x) then x + of None then return None + +## Type annotations and overloading allow us to define syntactic sugar for `Option[T]`, too. +pub macro ?(T: type) = + quote Option[`T`] +</code></pre> +<p>Compile-time programming may be done via the previously-mentioned <code>const</code> keyword and <code>when</code> statements: or via macros. Macros operate directly on the abstract syntax tree at compile-time: taking in syntax objects, transforming them, and returning them to be injected. They are <em>hygenic</em> and will not capture identifiers not passed as parameters. While parameters are syntax objects, they can be annotated with types to constrain applications of macros and allow for overloading. Macros are written in ordinary Puck: there is thus no need to learn a separate "macro language", as syntax objects are just standard <code>unions</code>. Additionally, support for <em>quoting</em> removes much of the need to operate on raw syntax objects. A full description may be found in the <a href="METAPROGRAMMING.html">metaprogramming document</a>.</p> <h2 id="async-system-and-threading"><a class="header" href="#async-system-and-threading">Async System and Threading</a></h2> <pre><code class="language-puck"></code></pre> <p>The async system is <em>colourblind</em>: the special <code>async</code> macro will turn any function <em>call</em> returning a <code>T</code> into an asynchronous call returning a <code>Future[T]</code>. The special <code>await</code> function will wait for any <code>Future[T]</code> and return a <code>T</code> (or an error). Async support is included in the standard library in <code>std.async</code> in order to allow for competing implementations. More details may be found in the <a href="ASYNC.html">async document</a>.</p> <p>Threading support is complex and also regulated to external libraries. OS-provided primitives will likely provide a <code>spawn</code> function, and there will be substantial restrictions for memory safety. I really haven't given much thought to this.</p> <h2 id="memory-management"><a class="header" href="#memory-management">Memory Management</a></h2> <pre><code class="language-puck"># Differences in Puck and Rust types in declarations and at call sights. +# note: this notation is not valid and is for illustrative purposes only func foo(a: lent T → &'a T mut T → &'a mut T @@ -321,7 +341,7 @@ foo( # this is usually elided ) </code></pre> <p>Puck copies Rust-style ownership near verbatim. <code>&T</code> corresponds to <code>lent T</code>, <code>&mut T</code> to <code>mut T</code>, and <code>T</code> to <code>T</code>: with <code>T</code> implicitly convertible to <code>lent T</code> and <code>mut T</code> 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, <em>is</em> a goal.)</p> -<p>Another major difference is the consolidation of <code>Box</code>, <code>Rc</code>, <code>Arc</code>, <code>Cell</code>, <code>RefCell</code> into just two (magic) types: <code>ref</code> and <code>refc</code>. <code>ref</code> takes the role of <code>Box</code>, and <code>refc</code> both the role of <code>Rc</code> and <code>Arc</code>: while <code>Cell</code> and <code>RefCell</code> are disregarded. The underlying motivation for compiler-izing these types is to make deeper compiler optimizations accessible: particularly with <code>refc</code>, 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 <a href="MEMORY_MANAGEMENT.html">memory management overview</a>.</p> +<p>Another major difference is the consolidation of <code>Box</code>, <code>Rc</code>, <code>Arc</code>, <code>Cell</code>, <code>RefCell</code> into just two (magic) types: <code>ref</code> and <code>refc</code>. <code>ref</code> takes the role of <code>Box</code>, and <code>refc</code> both the role of <code>Rc</code> and <code>Arc</code>: while <code>Cell</code> and <code>RefCell</code> are disregarded. The underlying motivation for compiler-izing these types is to make deeper compiler optimizations accessible: particularly with <code>refc</code>, where the existing ownership framework is used to eliminate unnecessary counts. Details on memory safety, references and pointers, and deep optimizations may be found in the <a href="MEMORY_MANAGEMENT.html">memory management overview</a>.</p> <h2 id="types-system"><a class="header" href="#types-system">Types System</a></h2> <pre><code class="language-puck"># The type Foo is defined here as an alias to a list of bytes. type Foo = list[byte] @@ -332,14 +352,14 @@ let foo: Foo = [1, 2, 3] func fancy_dbg(self: Foo) = print "Foo:" # iteration is defined for list[byte] - # so self is implicitly converted from Foo to list[byte] + # so it implicitly carries over: and is defined on Foo for elem in self do dbg(elem) # NO implicit conversion to Foo on calls [4, 5, 6].foo_dbg # this fails! -Foo([4, 5, 6]).foo_dbg # prints: Foo:\n 4\n\ 5\n 6\n +Foo([4, 5, 6]).foo_dbg # prints: Foo: 4 5 6 </code></pre> <p>Finally, a few notes on the type system are in order. Types are declared with the <code>type</code> keyword and are aliases: all functions defined on a type carry over to its alias, though the opposite is not true. Functions defined on the alias <em>must</em> take an object known to be a type of that alias: exceptions are made for type declarations, but at call sites this means that conversion must be explicit.</p> <pre><code class="language-puck"># We do not want functions defined on list[byte] to carry over, @@ -359,17 +379,23 @@ pub const MyAlias: type = VeryLongExampleType <p>If one wishes to define a new type <em>without</em> previous methods accessible, the newtype paradigm is preferred: declaring a single-field <code>struct</code>, and manually implementing functions that carry over. It can also be useful to have <em>transparent</em> type aliases, that is, simply a shorter name to refer to an existing type. These do not require type conversion, implicit or explicit, and can be used freely and interchangeably with their alias. This is done with constants.</p> <p>Types, like functions, can be <em>generic</em>: declared with "holes" that may be filled in with other types upon usage. A type must have all its holes filled before it can be constructed. The syntax for generics in types much resembles the syntax for generics in functions, and generic <em>constraints</em> and the like also apply.</p> <h2 id="structs-and-tuples"><a class="header" href="#structs-and-tuples">Structs and Tuples</a></h2> -<pre><code class="language-puck">type MyStruct = struct +<pre><code class="language-puck"># standard alternative syntax to inline declarations +type MyStruct = struct a: str b: str + +# syntactic sugar for tuple[str, b: str] type MyTuple = (str, b: str) let a: MyTuple = ("hello", "world") print a.1 # world print a.b # world + +let c: MyStruct = {a = a.0, b = a.1} +print c.b # world </code></pre> -<p>Struct and tuple types are declared with <code>struct[<fields>]</code> and <code>tuple[<fields>]</code>, respectively. Their declarations make them look similar at a glance, but they differ fairly fundamentally. Structs are <em>unordered</em>, and every field must be named. They may be constructed with <code>{}</code> brackets. Tuples are <em>ordered</em> and so field names are optional - names are just syntactic sugar for positional access (<code>foo.0</code>, <code>bar.1</code>, ...). Tuples are constructed with <code>()</code> parentheses: and also may be <em>declared</em> with such, as syntactic sugar for <code>tuple[...]</code>.</p> -<p>It is worth noting that there is no concept of <code>pub</code> at a field level on structs - a type is either fully transparent, or fully opaque. This is because such partial transparency breaks with structural initialization (how could one provide for hidden fields?). However, the <code>@[opaque]</code> attribute allows for expressing that the internal fields of a struct are not to be accessed or initialized: this, however, is only a compiler warning and can be totally suppressed with <code>@[allow(opaque)]</code>.</p> +<p>Struct and tuple types are declared with <code>struct[<fields>]</code> and <code>tuple[<fields>]</code>, respectively. Their declarations make them look similar at a glance: but they differ fairly fundamentally. Structs are <em>unordered</em> and every field must be named. They may be constructed with brackets. Tuples are <em>ordered</em> and so field names are optional - names are just syntactic sugar for positional access. Tuples are both constructed and optionally <em>declared</em> with parentheses.</p> +<p>It is worth noting that there is no concept of <code>pub</code> at a field level on structs - a type is either fully transparent, or fully opaque. This is because such partial transparency breaks with structural initialization (how could one provide for hidden fields?). The <code>@[opaque]</code> attribute allows for expressing that the internal fields of a struct are not to be accessed or initialized: this, however, is only a compiler warning and can be totally suppressed with <code>@[allow(opaque)]</code>.</p> <h2 id="unions-and-enums"><a class="header" href="#unions-and-enums">Unions and Enums</a></h2> <pre><code class="language-puck">type Expr = union Literal(int) @@ -380,7 +406,13 @@ print a.b # world <p>Union types are composed of a list of <em>variants</em>. Each variant has a <em>tag</em> and an <em>inner type</em> the union wraps over. Before the inner type can be accessed, the tag must be pattern matched upon, in order to handle all possible values. These are also known as <em>sum types</em> or <em>tagged unions</em> in other languages.</p> <p>Union types are the bread and butter of structural pattern matching. Composed with structs and tuples, unions provide for a very general programming construct commonly referred to as an <em>algebraic data type</em>. This is often useful as an idiomatic and safer replacement for inheritance.</p> -<pre><code class="language-puck"></code></pre> +<pre><code class="language-puck">type Opcode = enum + BRK INC POP NIP SWP ROT DUP OVR EQU NEQ GTH LTH JMP JCN JSR STH JCI JMI + LDZ STZ LDR STR LDA STA DEI DEO ADD SUB MUL DIV AND ORA EOR SFT JSI LIT + +print Opcode.BRK # 0 +... +</code></pre> <p>Enum types are similarly composed of a list of <em>variants</em>. These variants, however, are static values: assigned at compile-time, and represented under the hood by a single integer. They function similarly to unions, and can be passed through to functions and pattern matched upon, however their underlying simplicity and default values mean they are much more useful for collecting constants and acting as flags than anything else.</p> <h2 id="classes"><a class="header" href="#classes">Classes</a></h2> <pre><code class="language-puck">pub type Iter[T] = class @@ -393,7 +425,7 @@ pub type Peek[T] = class </code></pre> <p>Class types function much as type classes in Haskell or traits in Rust do. They are not concrete types, and cannot be constructed - instead, their utility is via indirection, as parameters in functions or as <code>ref</code> types in structures, providing constraints that some concrete type must meet. They consist of a list of function signatures, implementations of which must exist for the given type passed in in order to compile.</p> <p>Their major difference, however, is that Puck's classes are <em>implicit</em>: there is no <code>impl</code> block that implementations of their associated functions have to go under. If functions for a concrete type exist satisfying some class, the type implements that class. This does run the risk of accidentally implementing a class one does not desire to, but the author believes such situations are few and far between and well worth the decreased syntactic and semantic complexity. As a result, however, classes are entirely unable to guarantee any invariants hold (like <code>PartialOrd</code> or <code>Ord</code> in Rust do).</p> -<p>As the compiler makes no such distinction between fields and single-argument functions on a type when determining identifier conflicts, classes similarly make no such distinction. They <em>do</em> distinguish borrowed/mutable/owned parameters, those being part of the type signature.</p> +<p>As the compiler makes no such distinction between fields and single-argument functions on a type when determining identifier conflicts, classes similarly make no such distinction. Structs may be described with their fields written as methods. They <em>do</em> distinguish borrowed/mutable/owned parameters, those being part of the type signature.</p> <p>Classes are widely used throughout the standard library to provide general implementations of such conveniences like iteration, debug and display printing, generic error handling, and much more.</p> </main> |