diff options
author | JJ | 2024-01-28 09:32:52 +0000 |
---|---|---|
committer | JJ | 2024-01-28 09:32:52 +0000 |
commit | 22c4776f9fddef47a6ce3f309e4eafa2fbdc3a65 (patch) | |
tree | 49101d75a3f980eb8f8560f464f816a6fb9c564a /docs/book/OVERVIEW.html | |
parent | a409dd7ea7d6d363d5ef971526b81d95e6add51d (diff) |
docs: host mdbook
Diffstat (limited to 'docs/book/OVERVIEW.html')
-rw-r--r-- | docs/book/OVERVIEW.html | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/docs/book/OVERVIEW.html b/docs/book/OVERVIEW.html new file mode 100644 index 0000000..083e1f0 --- /dev/null +++ b/docs/book/OVERVIEW.html @@ -0,0 +1,405 @@ +<!DOCTYPE HTML> +<html lang="en" class="light" dir="ltr"> + <head> + <!-- Book generated using mdBook --> + <meta charset="UTF-8"> + <title>Basic Usage - The Puck Programming Language</title> + + + <!-- Custom HTML head --> + + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="theme-color" content="#ffffff"> + + <link rel="icon" href="favicon.svg"> + <link rel="shortcut icon" href="favicon.png"> + <link rel="stylesheet" href="css/variables.css"> + <link rel="stylesheet" href="css/general.css"> + <link rel="stylesheet" href="css/chrome.css"> + <link rel="stylesheet" href="css/print.css" media="print"> + + <!-- Fonts --> + <link rel="stylesheet" href="FontAwesome/css/font-awesome.css"> + <link rel="stylesheet" href="fonts/fonts.css"> + + <!-- Highlight.js Stylesheets --> + <link rel="stylesheet" href="highlight.css"> + <link rel="stylesheet" href="tomorrow-night.css"> + <link rel="stylesheet" href="ayu-highlight.css"> + + <!-- Custom theme stylesheets --> + + </head> + <body class="sidebar-visible no-js"> + <div id="body-container"> + <!-- Provide site root to javascript --> + <script> + var path_to_root = ""; + var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light"; + </script> + + <!-- Work around some values being stored in localStorage wrapped in quotes --> + <script> + try { + var theme = localStorage.getItem('mdbook-theme'); + var sidebar = localStorage.getItem('mdbook-sidebar'); + + if (theme.startsWith('"') && theme.endsWith('"')) { + localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1)); + } + + if (sidebar.startsWith('"') && sidebar.endsWith('"')) { + localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1)); + } + } catch (e) { } + </script> + + <!-- Set the theme before any content is loaded, prevents flash --> + <script> + var theme; + try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { } + if (theme === null || theme === undefined) { theme = default_theme; } + var html = document.querySelector('html'); + html.classList.remove('light') + html.classList.add(theme); + var body = document.querySelector('body'); + body.classList.remove('no-js') + body.classList.add('js'); + </script> + + <input type="checkbox" id="sidebar-toggle-anchor" class="hidden"> + + <!-- Hide / unhide sidebar before it is displayed --> + <script> + var body = document.querySelector('body'); + var sidebar = null; + var sidebar_toggle = document.getElementById("sidebar-toggle-anchor"); + if (document.body.clientWidth >= 1080) { + try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { } + sidebar = sidebar || 'visible'; + } else { + sidebar = 'hidden'; + } + sidebar_toggle.checked = sidebar === 'visible'; + body.classList.remove('sidebar-visible'); + body.classList.add("sidebar-" + sidebar); + </script> + + <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> + </div> + <div id="sidebar-resize-handle" class="sidebar-resize-handle"> + <div class="sidebar-resize-indicator"></div> + </div> + </nav> + + <!-- Track and set sidebar scroll position --> + <script> + var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox'); + sidebarScrollbox.addEventListener('click', function(e) { + if (e.target.tagName === 'A') { + sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop); + } + }, { passive: true }); + var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll'); + sessionStorage.removeItem('sidebar-scroll'); + if (sidebarScrollTop) { + // preserve sidebar scroll position when navigating via links within sidebar + sidebarScrollbox.scrollTop = sidebarScrollTop; + } else { + // scroll sidebar to current active section when navigating via "next/previous chapter" buttons + var activeSection = document.querySelector('#sidebar .active'); + if (activeSection) { + activeSection.scrollIntoView({ block: 'center' }); + } + } + </script> + + <div id="page-wrapper" class="page-wrapper"> + + <div class="page"> + <div id="menu-bar-hover-placeholder"></div> + <div id="menu-bar" class="menu-bar sticky"> + <div class="left-buttons"> + <label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar"> + <i class="fa fa-bars"></i> + </label> + <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list"> + <i class="fa fa-paint-brush"></i> + </button> + <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu"> + <li role="none"><button role="menuitem" class="theme" id="light">Light</button></li> + <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li> + <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li> + <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li> + <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li> + </ul> + <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar"> + <i class="fa fa-search"></i> + </button> + </div> + + <h1 class="menu-title">The Puck Programming Language</h1> + + <div class="right-buttons"> + <a href="print.html" title="Print this book" aria-label="Print this book"> + <i id="print-button" class="fa fa-print"></i> + </a> + + </div> + </div> + + <div id="search-wrapper" class="hidden"> + <form id="searchbar-outer" class="searchbar-outer"> + <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header"> + </form> + <div id="searchresults-outer" class="searchresults-outer hidden"> + <div id="searchresults-header" class="searchresults-header"></div> + <ul id="searchresults"> + </ul> + </div> + </div> + + <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM --> + <script> + document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible'); + document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible'); + Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) { + link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1); + }); + </script> + + <div id="content" class="content"> + <main> + <h1 id="an-overview-of-puck"><a class="header" href="#an-overview-of-puck">An Overview of Puck</a></h1> +<p>Puck is an experimental, high-level, memory-safe, statically-typed, whitespace-sensitive, interface-oriented, imperative programming language with functional underpinnings. </p> +<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> +<pre><code class="language-puck">let ident: int = 413 +# type annotations are optional +var phrase = "Hello, world!" +const compile_time = when linux: "linux" else: "windows" +</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. +Variables are conventionally written in <code>snake_case</code>. Types are conventionally written in <code>PascalCase</code>. +The type system is comprehensive, and complex enough to warrant delaying full coverage of until the end. Some basic types are of note, however:</p> +<ul> +<li><code>int</code>, <code>uint</code>: signed and unsigned integers +<ul> +<li><code>i8</code>/<code>i16</code>/<code>i32</code>/<code>i64</code>/<code>i128</code>: their fixed-size counterparts</li> +<li><code>u8</code>/<code>u16</code>/<code>u32</code>/<code>u64</code>/<code>u128</code>: their fixed-size counterparts</li> +</ul> +</li> +<li><code>float</code>, <code>decimal</code>: floating-point numbers +<ul> +<li><code>f32</code>/<code>f64</code>/<code>f128</code>: their fixed-size counterparts</li> +<li><code>dec64</code>/<code>dec128</code>: their fixed-size counterparts</li> +</ul> +</li> +<li><code>byte</code>: an alias to <code>u8</code>, representing one byte</li> +<li><code>chr</code>: an alias to <code>u32</code>, representing one Unicode character</li> +<li><code>bool</code>: defined as <code>union[false, true]</code></li> +<li><code>array[T, S]</code>: primitive fixed-size (<code>S</code>) arrays</li> +<li><code>list[T]</code>: dynamic lists</li> +<li><code>str</code>: mutable strings. internally a <code>list[byte]</code>, externally a <code>list[chr]</code></li> +<li><code>slice[T]</code>: borrowed "views" into the three types above</li> +</ul> +<p>Comments are declared with <code>#</code> and run until the end of the line. +Documentation comments are declared with <code>##</code> and may be parsed by language servers and other tooling. +Multi-line comments are declared with <code>#[ ]#</code> and may be nested. +Taking cues from the Lisp family of languages, any expression may be commented out with a preceding <code>#;</code>.</p> +<pre><code class="language-puck"></code></pre> +<p>Functions are declared with the <code>func</code> keyword. They take an (optional) list of generic parameters (in brackets), an (optional) list of parameters (in parentheses), and <strong>must</strong> 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 <code>mut</code> or <code>static</code>: denoting a <em>mutable</em> type (types are copied into functions and thus immutable by default), or a <em>static</em> type (known to the compiler at compile time, and usable in <code>const</code> exprs). Generic parameters may each be optionally annotated with a type functioning as a <em>constraint</em>.</p> +<!-- 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. --> +<p>Whitespace is significant but flexible: functions may be declared entirely on one line if so desired. A new level of indentation after certain tokens (<code>:</code>, <code>=</code>) denotes a new level of scope. There are some places where arbitrary indentation and line breaks are allowed - as a general rule of thumb, after operators, commas, and opening parentheses. The particular rules governing indentation may be found in the <a href="SYNTAX.html#indentation-rules">syntax guide</a>.</p> +<pre><code class="language-puck">func inc(self: list[int], by: int): list[int] = + self.map(x => x + by) + +print inc([1, 2, 3], len("four")) # 5, 6, 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> +<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> +<ul> +<li>the words <code>and</code>/<code>or</code>/<code>not</code>/<code>shl</code>/<code>shr</code> are used instead of the symbolic <code>&&</code>/<code>||</code>/<code>!</code>/<code><<</code>/<code>>></code></li> +<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</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 in the current scope (more on this in the <a href="TYPES.html">types document</a>). <!-- Membership of collections is expressed with `in`, and is overloaded for most types. --></p> +<pre><code class="language-puck">let phrase: str = "I am a string! Wheeee! ✨" +for c in phrase: + stdout.write(c) # I am a string! Wheeee! ✨ +for b in phrase.bytes(): + stdout.write(b.chr) # Error: cannot convert between u8 and chr +print phrase.last() # ✨ +</code></pre> +<p>String concatenation uses a distinct <code>&</code> operator rather than overloading the <code>+</code> operator (as the complement <code>-</code> has no natural meaning for strings). Strings are unified, mutable, internally a byte array, externally a char array, and are stored as a pointer to heap data after their length and capacity (fat pointer). Chars are four bytes and represent a Unicode character in UTF-8 encoding. Slices of strings are stored as a length followed by a pointer to string data, and have non-trivial interactions with the memory management system. More details can be found in the <a href="TYPES.html">type system overview</a>.</p> +<pre><code class="language-puck"></code></pre> +<p>Basic conditional control flow uses standard <code>if</code>/<code>elif</code>/<code>else</code> statements. The <code>when</code> statement provides a compile-time <code>if</code>. It also takes <code>elif</code> and <code>else</code> branches and is syntactic sugar for an <code>if</code> statement within a <code>static</code> block (more on those later).</p> +<p>All values in Puck must be handled, or explicitly discarded. This allows for conditional statements and many other control flow constructs to function as <em>expressions</em>, 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 <em>functions</em>, where it is often idiomatic to omit an explicit <code>return</code> statement. There is no attempt made to differentiate without context, and so expressions and statements often look identical in syntax.</p> +<pre><code class="language-puck"></code></pre> +<p>Exhaustive structural pattern matching is available with the <code>match</code>/<code>of</code> statement, and is particularly useful for the <code>struct</code> and <code>union</code> types. <code>of</code> branches of a <code>match</code> statement take a <em>pattern</em>, of which the unbound identifiers within will be injected into the branch's scope. Multiple patterns may be used for one branch provided they all bind the same identifiers of the same type. Branches may be <em>guarded</em> with the <code>where</code> keyword, which takes a conditional, and will necessarily remove the branch from exhaustivity checks.</p> +<!-- todo: structural matching of lists and arrays --> +<p>The <code>of</code> statement also stands on its own as an operator for querying subtype equality. Used as a conditional in <code>if</code> statements or <code>while</code> loops, it retains the variable injection properties of its <code>match</code> counterpart. This allows it to be used as a compact <!-- and coherent --> alternative to <code>if let</code> statements in other languages.</p> +<pre><code class="language-puck">func may_fail: Result[T, ref Err] +</code></pre> +<p>Error handling is done via a fusion of imperative <code>try</code>/<code>catch</code> statements and functional <code>Option</code>/<code>Result</code> types, with much syntactic sugar. Functions may <code>raise</code> errors, but should return <code>Option[T]</code> or <code>Result[T, E]</code> types instead by convention. The compiler will note functions that <code>raise</code> errors, and force explicit qualification of them via <code>try</code>/<code>catch</code> statements.</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. (There is additionally another <code>?</code> postfix macro, taking in a type, as a shorthand for <code>Option[T]</code>)</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>catch</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> +<pre><code class="language-puck">loop: + print "This will never normally exit." + break + +for i in 0 .. 3: # exclusive + for j in 0 ..= 3: # inclusive + 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> interface (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 y = read_input() + transform_input(y) + +block foo: + for i in 0 ..= 100: + block bar: + if i == 10: break foo + print i +</code></pre> +<p>Blocks provide arbitrary scope manipulation. They may be labelled or unlabelled. The <code>break</code> keyword additionally functions inside of blocks and without any parameters will jump out of the current enclosing block (or loop). It may also take a block label as a parameter for fine-grained scope control.</p> +<pre><code class="language-puck"></code></pre> +<p>Code is segmented into modules. Modules may be made explicit with the <code>mod</code> keyword followed by a name, but there is also an implicit module structure in every codebase that follows the structure and naming of the local filesystem. For compatibility with filesystems, and for consistency, module names are exclusively lowercase (following the same rules as Windows).</p> +<p>A module can be imported into another module by use of the <code>use</code> keyword, taking a path to a module or modules. Contrary to the majority of languages ex. Python, unqualified imports are <em>encouraged</em> - in fact, are idiomatic (and the default) - type-based disambiguation and official LSP support are intended to remove any ambiguity.</p> +<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, i.e. <code>use my_module; pub const my_module = my_module</code>.</p> +<p>More details may be found in the <a href="MODULES.html">modules document</a>.</p> +<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.</p> +<p>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 <a href="METAPROGRAMMING.html">metaprogramming document</a>.</p> +<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> +<pre><code class="language-puck"></code></pre> +<p>Details on memory safety, references and pointers, and deep optimizations may be found in the <a href="MEMORY_MANAGEMENT.html">memory management overview</a>. +The memory model intertwines deeply with the type system. <!-- todo --></p> +<pre><code class="language-puck"></code></pre> +<p>Finally, a few notes on the type system are in order.</p> +<p>Types are declared with the <code>type</code> keyword and are transparent aliases. +That is, <code>type Foo = Bar</code> means that any function defined for <code>Bar</code> is defined for <code>Foo</code> - that is, objects of type <code>Foo</code> can be used any time an object of type <code>Bar</code> is called for. +If such behavior is not desired, the <code>distinct</code> keyword forces explicit qualification and conversion of types. <code>type Foo = distinct Baz</code> will force a type <code>Foo</code> to be wrapped in a call to the constructor <code>Baz()</code> before being passed to such functions.</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 <em>constraints</em> and the like also apply.</p> +<pre><code class="language-puck">type MyStruct = struct + a: str + b: str +type MyTuple = tuple[str, b: str] + +let a: MyTuple = ("hello", "world") +print a.1 # world +print a.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. Tuples may be constructed with <code>()</code> parenthesis.</p> +<p>I am undecided whether to allow <em>structural subtyping</em>: that is, <code>{a: Type, b: Type, c: Type}</code> being valid in a context expecting <code>{a: Type, b: Type}</code>. This has benefits (multiple inheritance with no boilerplate) but also downsides (obvious).</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?). An idiomatic workaround is to model the desired field structure with a public-facing interface.</p> +<pre><code class="language-puck">type Expr = union + Literal(int) + Variable(str) + Abstraction(param: str, body: ref Expr) + Application(body: ref Expr, arg: ref Expr) +</code></pre> +<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">pub type Iter[T] = interface + next(mut Self): T? + +pub type Peek[T] = interface + next(mut Self): T? + peek(mut Self): T? + peek_nth(mut Self, int): T? +</code></pre> +<p>Interface 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 or as <code>ref</code> types, 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 in order to compile.</p> +<p>Their major difference, however, is that Puck's interfaces 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 interface, the type implements that interface. This does run the risk of accidentally implementing an interface one does not desire to, but the author believes such situations are few and far between, well worth the decreased syntactic and semantic complexity, and mitigatable with tactical usage of the <code>distinct</code> keyword.</p> +<p>As the compiler makes no such distinction between fields and single-argument functions on a type when determining identifier conflicts, interfaces similarly make no such distinction. They <em>do</em> distinguish mutable and immutable parameters, those being part of the type signature.</p> +<p>Interfaces 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> + + <nav class="nav-wrapper" aria-label="Page navigation"> + <!-- Mobile navigation buttons --> + <a rel="prev" href="../index.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left"> + <i class="fa fa-angle-left"></i> + </a> + + <a rel="next prefetch" href="SYNTAX.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right"> + <i class="fa fa-angle-right"></i> + </a> + + <div style="clear: both"></div> + </nav> + </div> + </div> + + <nav class="nav-wide-wrapper" aria-label="Page navigation"> + <a rel="prev" href="../index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left"> + <i class="fa fa-angle-left"></i> + </a> + + <a rel="next prefetch" href="SYNTAX.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right"> + <i class="fa fa-angle-right"></i> + </a> + </nav> + + </div> + + <!-- Livereload script (if served using the cli tool) --> + <script> + const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:'; + const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload"; + const socket = new WebSocket(wsAddress); + socket.onmessage = function (event) { + if (event.data === "reload") { + socket.close(); + location.reload(); + } + }; + + window.onbeforeunload = function() { + socket.close(); + } + </script> + + + + <script> + window.playground_copyable = true; + </script> + + + <script src="elasticlunr.min.js"></script> + <script src="mark.min.js"></script> + <script src="searcher.js"></script> + + <script src="clipboard.min.js"></script> + <script src="highlight.js"></script> + <script src="book.js"></script> + + <!-- Custom JS scripts --> + + + </div> + </body> +</html> |