what
This commit is contained in:
parent
864544624c
commit
a3e82f34b5
246 changed files with 4433 additions and 2 deletions
549
acl.cool/site/OVERVIEW_GP1.html
Normal file
549
acl.cool/site/OVERVIEW_GP1.html
Normal file
|
@ -0,0 +1,549 @@
|
|||
<h1 id="programming-language-general-purpose-1">Overview of the <br>GP1 Programming Language</h1>
|
||||
<p>GP1 is a statically typed, multi-paradigm programming language with
|
||||
an emphasis on brevity and explicitness. It provides both value and
|
||||
reference types, as well as higher-order functions and first-class
|
||||
support for many common programming patterns.</p><p>This document serves as a quick, informal reference for developers of GP1 (or anyone who's curious).</p>
|
||||
<h2 id="variables-and-constants">Variables and Constants</h2>
|
||||
<p>A given "variable" is defined with either the <code>var</code> or
|
||||
<code>con</code> keyword, for mutable and immutable assignment
|
||||
respectively, alonside the assignment operator, <code><-</code>. An
|
||||
uninitialized variable MUST have an explicit type, and cannot be
|
||||
accessed until it is assigned. A variable that is initialized in its
|
||||
declaration may have an explicit type, but the type may be inferred
|
||||
here, when possible, if one is omitted. Normal type-coercion rules apply
|
||||
in assignments, as described in the <em>Coercion and Casting</em>
|
||||
section.</p>
|
||||
<p>Non-ascii unicode characters are allowed in variable names as long as
|
||||
the character doesn't cause a parsing issue. For example, whitespace
|
||||
tokens are not allowed in variable names.</p>
|
||||
<p>Some examples of assigning variables:</p>
|
||||
<pre class="gp1"><code>var x: i32; // x is an uninitialized 32-bit signed integer
|
||||
var y <- x; // this won't work, because x has no value
|
||||
x <- 7;
|
||||
var y <- x; // this time it works, because x is now 7
|
||||
|
||||
con a: f64 <- 99.8; // a is immutable
|
||||
a <- 44.12; // this doesn't work, because con variables cannot be reassigned</code></pre>
|
||||
<p>The following lines are equivalent,</p>
|
||||
<pre class="gp1"><code>con a <- f64(7.2);
|
||||
con a: f64 <- 7.2;
|
||||
con a <- 7.2; // 7.2 is implicitly of type f64
|
||||
con a <- 7.2D; // With an explicit type suffix</code></pre>
|
||||
<p>as are these.</p>
|
||||
<pre class="gp1"><code>var c: f32 <- 9;
|
||||
var c <- f32(9);
|
||||
var c: f32 <- f32(9);
|
||||
var c <- 9F;</code></pre>
|
||||
<p>Variable assignments are expressions in GP1, which can enable some
|
||||
very interesting code patterns. For example, it allows multiple
|
||||
assignments on one line with the following syntax.
|
||||
<code>con a <- var b <- "death and taxes"</code> assigns the
|
||||
string <code>"death and taxes"</code> to both <code>a</code> and
|
||||
<code>b</code>, leaving you with one constant and one variable
|
||||
containing separate instances of identical data. This is equivalent to
|
||||
writing <code>con a <- "death and taxes"</code> and
|
||||
<code>var b <- "death and taxes"</code> each on their own line.
|
||||
Assignment as an expression also eliminates much of the need to define
|
||||
variables immediately before the control structure in which they're
|
||||
used, which improves readability.</p>
|
||||
<h2 id="intrinsic-types">Intrinsic Types</h2>
|
||||
<h3 id="numeric-types">Numeric Types</h3>
|
||||
<p><code>u8</code> <code>u16</code> <code>u32</code> <code>u64</code>
|
||||
<code>u128</code> <code>u256</code> <code>usize</code>
|
||||
<code>byte</code></p>
|
||||
<p><code>i8</code> <code>i16</code> <code>i32</code> <code>i64</code>
|
||||
<code>i128</code> <code>i256</code> <code>isize</code></p>
|
||||
<p><code>f16</code> <code>f32</code> <code>f64</code> <code>f128</code>
|
||||
<code>f256</code></p>
|
||||
<p>GP1 has signed integer, unsigned integer, and floating point numeric
|
||||
types. Numeric types take the form of a single-letter indicator followed
|
||||
by the type's size in bits. The indicators are <strong>i</strong>
|
||||
(signed integer), <strong>u</strong> (unsigned integer), and
|
||||
<strong>f</strong> (floating point). <code>usize</code> and
|
||||
<code>isize</code> are pointer-width types. For example, on a 64-bit
|
||||
system, <code>usize</code> is a 64-bit unsigned integer. However, it
|
||||
must be cast to <code>u64</code> when assigning to a <code>u64</code>
|
||||
variable. The type <code>byte</code> is an alias for <code>u8</code>.
|
||||
Numeric operators are as one expects from C, with the addition of
|
||||
<code>**</code> as a power operator.</p>
|
||||
<p>Numeric literals have an implicit type, or the type can be specified
|
||||
by a case-insensitive suffix. For example:</p>
|
||||
<pre class="gp1"><code>var i1 <- 1234; // implicitly i32
|
||||
var f1 <- 1234.5; // implicitly f64
|
||||
|
||||
var i3 <- 1234L; // i64
|
||||
var u3 <- 1234ui; // u32
|
||||
var f2 <- 1234.6F; // f32</code></pre>
|
||||
<p>The complete set of suffixes is given.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th>suffix</th>
|
||||
<th>corresponding type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td>s</td>
|
||||
<td>i16</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>i</td>
|
||||
<td>i32</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td>l</td>
|
||||
<td>i64</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>p</td>
|
||||
<td>isize</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td>b</td>
|
||||
<td>byte</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>us</td>
|
||||
<td>u16</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td>ui</td>
|
||||
<td>u32</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>ul</td>
|
||||
<td>u64</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td>up</td>
|
||||
<td>usize</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>f</td>
|
||||
<td>f32</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td>d</td>
|
||||
<td>f64</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>q</td>
|
||||
<td>f128</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="booleans">Booleans</h3>
|
||||
<p><code>bool</code> is the standard boolean type with support for all
|
||||
the usual operations. The boolean literals are <code>true</code> and
|
||||
<code>false</code>. Bool operators are as one expects from C, with the
|
||||
exception that NOT is <code>!!</code> instead of <code>!</code>.</p>
|
||||
<h3 id="bitwise-operators">Bitwise Operators</h3>
|
||||
<p>Bitwise operators can be applied only to integers and booleans. They
|
||||
are single counterparts of the doubled boolean operators, e.g. boolean
|
||||
negation is <code>!!</code>, so bitwise negation is <code>!</code>.</p>
|
||||
<h3 id="strings-and-characters">Strings and Characters</h3>
|
||||
<p><code>char</code> is a unicode character of variable size. Char
|
||||
literals are single-quoted, e.g. <code>'c'</code>. Any single valid char
|
||||
value can be used as a literal in this fasion.</p>
|
||||
<p><code>string</code> is a unicode string. String literals are
|
||||
double-quoted, e.g. <code>"Hello, World."</code>.</p>
|
||||
<h3 id="arrays">Arrays</h3>
|
||||
<p>GP supports typical array operations.</p>
|
||||
<pre class="gp1"><code>var tuples : (int, int)[]; // declare array of tuples
|
||||
var strings : string[]; // declare array of strings
|
||||
|
||||
var array <- i32[n]; // declare and allocate array of n elements
|
||||
// n is any number that can be coerced to usize
|
||||
|
||||
con nums <- {1, 2, 3}; // immutable array of i32
|
||||
</code></pre>
|
||||
<p>Use the <code>length</code> property to access the number of elements
|
||||
in an allocated array. Attempting to access <code>length</code> of an
|
||||
unallocated array is an exception.</p>
|
||||
<pre class="gp1"><code>
|
||||
var colors <- {"Red", "White", "Blue"}; // allocate array
|
||||
|
||||
var count <- colors.length; // count is usize(3)
|
||||
</code></pre>
|
||||
<p>Arrays can be indexed with any integer type (signed or unsigned).
|
||||
Negative values wrap from the end (-1 is the last element). An exception
|
||||
occurs if the value is too big, i.e.no modulo operation is
|
||||
performed.</p>
|
||||
<pre class="gp1"><code>var w <- {1, 2, 3, 4, 5, 6, 7};
|
||||
|
||||
w[0] // first element, 1
|
||||
w[-1] // last element, 7
|
||||
|
||||
var x <- isize(-5);
|
||||
w[x] // 5th to last element, 3
|
||||
</code></pre>
|
||||
<h3 id="tuples">Tuples</h3>
|
||||
<p>Tuples group multiple values into a single value with anonymous,
|
||||
ordered fields. <code>()</code> is an empty tuple.
|
||||
<code>("hello", i32(17))</code> is a tuple of type
|
||||
<code>(string i32)</code>. Tuple fields are named like indices,
|
||||
i.e.<code>(u128(4), "2").1</code> would be <code>"2"</code>.</p>
|
||||
<p>The unit type, represented as a 0-tuple, is written
|
||||
<code>()</code>.</p>
|
||||
<h3 id="regex">Regex</h3>
|
||||
<p><code>regex</code> is a regular expression. GP1 regex format is
|
||||
identical to that of .NET 5 and very similar to that of gawk.</p>
|
||||
<h2 id="named-functions">Named Functions</h2>
|
||||
<p>Some examples of defining named functions:</p>
|
||||
<pre class="gp1"><code>fn sum(a: f32, b: f32): f32 { a + b } // takes parameters and returns an f32
|
||||
|
||||
fn twice_println(s: string) { // takes parameters and implicitly returns ()
|
||||
println("${s}\n${s}");
|
||||
}
|
||||
|
||||
fn join_println(a: string, b: string): () { // takes parameters and explicitly returns ()
|
||||
println("${a} ${b}");
|
||||
}
|
||||
|
||||
fn seven(): u32 { 7 } // takes no parameters and returns the u32 value of 7</code></pre>
|
||||
<p>There are a number of syntaxes allowed for calling a given function.
|
||||
This is because the caller is allowed to assign to zero or more of that
|
||||
function's parameters by name. Parameters assigned by name are freely
|
||||
ordered, while those assigned normally bind to the first parameter
|
||||
ordered from left to right in the function definition that is
|
||||
unassigned. With regard to the <code>join_println</code> function
|
||||
defined above, this means that all of the following are valid and behave
|
||||
identically.</p>
|
||||
<pre class="gp1"><code>join_println(a <- "Hello,", b <- "World.");
|
||||
join_println(b <- "World.", a <- "Hello,");
|
||||
join_println(b <- "World.", "Hello,");
|
||||
join_println("Hello,", "World.");</code></pre>
|
||||
<p>Function names may be overloaded. For example,
|
||||
<code>join_println</code> could be additionally defined as</p>
|
||||
<pre class="gp1"><code>fn join_println(a: string, b: string, sep: string) {
|
||||
println("${a}${sep}${b}");
|
||||
}</code></pre>
|
||||
<p>and then both <code>join_println("Hello,", "World.", " ")</code> and
|
||||
<code>join_println("Hello,", "World.")</code> would be valid calls.</p>
|
||||
<p>Functions may be defined and called within other functions. You may
|
||||
be familar with this pattern from functional languages like F#, wherein
|
||||
a wrapper function is often used to guard an inner recursive function
|
||||
(GP1 permits both single and mutual recursion in functions). For
|
||||
example:</p>
|
||||
<pre class="gp1"><code>fn factorial(n: u256): u256 {
|
||||
fn aux(n: u256, accumulator: u256): u256 {
|
||||
match n > 1 {
|
||||
true => aux(n - 1, accumulator * n),
|
||||
_ => accumulator,
|
||||
}
|
||||
}
|
||||
aux(n, 1)
|
||||
}</code></pre>
|
||||
<p>Arguments are passed by value by default. For information on the
|
||||
syntax used in this example, refer to <em>Control Flow</em>.</p>
|
||||
<h2 id="anonymous-functions">Anonymous Functions</h2>
|
||||
<h3 id="closures">Closures</h3>
|
||||
<p>Closures behave as one would expect in GP1, exactly like they do in
|
||||
most other programming languages that feature them. Closures look like
|
||||
this:</p>
|
||||
<pre class="gp1"><code>var x: u32 <- 8;
|
||||
|
||||
var foo <- { y, z => x * y * z}; // foo is a closure; its type is fn<u32 | u32>
|
||||
assert(foo(3, 11) == (8 * 3 * 11)); // true
|
||||
|
||||
x <- 5;
|
||||
assert(foo(3) == (8 * 3 * 11)); // true
|
||||
|
||||
con bar <- { => x * x }; // bar is a closure of type `fn<u32>`
|
||||
|
||||
assert(bar() == 25); // true because closure references already-defined x</code></pre>
|
||||
<p>They are surrounded by curly braces. Within the curly braces goes an
|
||||
optional, comma-separated parameter list, followed by a required
|
||||
<code>=></code> symbol, followed by an optional expression. If no
|
||||
expression is included, the closure implicitly returns
|
||||
<code>()</code>.</p>
|
||||
<p>The reason the match-expression uses the same <code>=></code>
|
||||
symbol is because the <code>when</code> section of a match arm is an
|
||||
implicit closure. The reason <code>=></code> in particular was chosen
|
||||
for closures is twofold. One, arrows are conventional for expressing
|
||||
anonymous functions, and two, the space between the lines of an equals
|
||||
sign is enclosed by them.</p>
|
||||
<h3 id="lambdas">Lambdas</h3>
|
||||
<p>Lambdas are nearly identical to closures, but they don't close over
|
||||
their environment, and they use the <code>-></code> symbol in place
|
||||
of <code>=></code>. A few examples of lambdas:</p>
|
||||
<pre class="gp1"><code>con x: u32 <- 4; // this line is totally irrelevant
|
||||
|
||||
con square <- { x -> x * x }; // this in not valid, because the type of the function is not known
|
||||
con square <- { x: u32 -> x * x }; // this if fine, because the type is specified in the lambda
|
||||
con square: fn<u32 | u32> <- { x -> x * x }; // also fine, because the type is specified in the declaration</code></pre>
|
||||
<h2 id="function-types">Function Types</h2>
|
||||
<p>Functions are first-class citizens in GP1, so you can assign them to
|
||||
variables, pass them as arguments, &c.However, using the function
|
||||
definition syntax is suboptimal when using function types. Instead,
|
||||
there is a separate syntax for function types. Given the function
|
||||
<code>fn sum(a: f64, b: f64): f64 { a + b }</code> the function type is
|
||||
expressed <code>fn<f64 f64 | f64></code>, meaning a function that
|
||||
accepts two f64 values and returns an f64. Therefore,</p>
|
||||
<pre class="gp1"><code>fn sum(a: f64, b: f64): f64 { a + b } </code></pre>
|
||||
<pre class="gp1"><code>con sum: fn<f64 f64 | f64> <- { a, b -> a + b };</code></pre>
|
||||
<pre class="gp1"><code>con sum <- { a: f64, b: f64 -> a + b };</code></pre>
|
||||
<p>are all equivalent ways of binding a function of type
|
||||
<code>fn<f64 f64 | f64></code> to the constant <code>sum</code>.
|
||||
Here's an example of how to express a function type for a function
|
||||
argument.</p>
|
||||
<pre class="gp1"><code>fn apply_op(a: i32, b: i32, op: fn<i32 i32 | i32>): i32 {
|
||||
op(a, b)
|
||||
}</code></pre>
|
||||
<h3 id="function-type-inference">Function Type Inference</h3>
|
||||
<p>The above example provides an explicit type for the argument
|
||||
<code>op</code>. You could safely rewrite this as</p>
|
||||
<pre class="gp1"><code>fn apply_op(a: i32, b: i32, op: fn): i32 {
|
||||
op(a, b)
|
||||
}</code></pre>
|
||||
<p>because the compiler can safely infer the function type of
|
||||
<code>op</code>. Type inference only works to figure out the function
|
||||
signature, so <code>fn apply_op(a:i32, b:i32, op):i32 { . . . }</code>
|
||||
is not allowed.</p>
|
||||
<h2 id="coercion-and-casting">Coercion and Casting</h2>
|
||||
<p>Refer to <em>Variables and Constants</em> for information on the
|
||||
syntax used in this section.</p>
|
||||
<p>Numeric types are automatically coerced into other numeric types as
|
||||
long as that coercion is not lossy. For example,</p>
|
||||
<pre class="gp1"><code>var x: i32 <- 10;
|
||||
var y: i64 <- x;</code></pre>
|
||||
<p>is perfectly legal (the 32-bit value fits nicely in the 64-bit
|
||||
variable). However, automatic coercion doesn't work if it would be
|
||||
lossy, so</p>
|
||||
<pre class="gp1"><code>var x: i64 <- 10;
|
||||
var y: i32 <- x;</code></pre>
|
||||
<p>doesn't work. This holds for numeric literals as well.
|
||||
Unsurprisingly, <code>var x: i32 <- 3.14</code> wouldn't compile. The
|
||||
floating point value can't be automatically coerced to an integer type.
|
||||
So what does work? Casting via the target type's pseudo-constructor
|
||||
works.</p>
|
||||
<pre class="gp1"><code>con x: f64 <- 1234.5; // okay because the literal can represent any floating point type
|
||||
con y: f64 <- f16(1234.5); // also okay, because any f16 can be losslessly coerced to an f64
|
||||
con z: i32 <- i32(x); // also okay; uses the i32 pseudo-constructor to 'cast' x to a 32-bit integer
|
||||
|
||||
assert(z == 1234)
|
||||
|
||||
con a: f64 <- 4 * 10 ** 38; // this value is greater than the greatest f32
|
||||
con b: f32 <- f32(a); // the value of b is the maximum value of f32</code></pre>
|
||||
<p>This approach is valid for all intrinsic types. For example,
|
||||
<code>var flag: bool <- bool(0)</code> sets <code>flag</code> to
|
||||
<code>false</code> and <code>var txt: string <- string(83.2)</code>
|
||||
sets <code>txt</code> to the string value <code>"83.2"</code>. Such
|
||||
behavior can be implemented by a programmer on their own types via a
|
||||
system we'll discuss in the <em>Interfaces</em> section.</p>
|
||||
<h2 id="program-structure">Program Structure</h2>
|
||||
<p>Every GP1 program has an entry-point function. Within that function,
|
||||
statements are executed from top to bottom and left to right. The
|
||||
entry-point function can be declared with the <code>entry</code> keyword
|
||||
in place of <code>fn</code> and returns an integer, which will be
|
||||
provided to the host operating system as an exit code. Naturally, this
|
||||
means that the handling of that code is platform-dependent once it
|
||||
passes the program boundry, so it's important to keep in mind that a
|
||||
system may implicitly downcast or otherwise modify it before it is made
|
||||
available to the user. If no exit code is specified, or if the return
|
||||
type of the function is not an integer, GP1 assumes an exit code of
|
||||
<code>usize(0)</code> and returns that to the operating system.</p>
|
||||
<p>The following program prints Hello, World. and exits with an error
|
||||
code.</p>
|
||||
<pre class="gp1"><code>entry main(): usize {
|
||||
hello_world();
|
||||
1
|
||||
}
|
||||
|
||||
fn hello_world() {
|
||||
println("Hello, World.");
|
||||
}</code></pre>
|
||||
<p>The entry function may have any name; it's the <code>entry</code>
|
||||
keyword that makes it the entry point. The entry function may also be
|
||||
implicit. If one is not defined explicitly, the entire file is treated
|
||||
as being inside an entry function. Therefore,</p>
|
||||
<pre class="gp1"><code>println("Hello, World.");</code></pre>
|
||||
<p>is a valid and complete program identical to</p>
|
||||
<pre class="gp1"><code>entry main(): usize {
|
||||
println("Hello, World.");
|
||||
}</code></pre>
|
||||
<p>This behavior can lend GP1 a very flexible feeling akin to many
|
||||
scripting languages.</p>
|
||||
<p>In a program where there is an entry-point specified, only
|
||||
expressions made within that function will be evaluated. This means that
|
||||
the following program does NOT print anything to the console.</p>
|
||||
<pre class="gp1"><code>entry main(): usize {
|
||||
con x: usize <- 7;
|
||||
}
|
||||
|
||||
println("This text will not be printed.");</code></pre>
|
||||
<p>In fact, this program is invalid. Whenever there is an explicit entry
|
||||
point, no statements may be made in the global scope.</p>
|
||||
<h2 id="control-flow">Control Flow</h2>
|
||||
<h3 id="conditionals">Conditionals</h3>
|
||||
<p>At this time, GP1 has only one non-looping conditional control
|
||||
structure, in two variants: <code>match</code> and
|
||||
<code>match all</code>. The syntax is as follows, where
|
||||
<code>*expr*</code> are expressions and <code>pattern*</code> are
|
||||
pattern matching options (refer to <em>Pattern Matching</em> for more
|
||||
info).</p>
|
||||
<pre class="gp1"><code>match expr {
|
||||
pattern1 => arm_expr1,
|
||||
pattern2 => arm_expr2,
|
||||
_ => arm_expr3,
|
||||
}</code></pre>
|
||||
<p>The <code>match</code> expression executes the first arm that matches
|
||||
the pattern passed in <code>expr</code>. The <code>match all</code>
|
||||
expression executes all arms that match the pattern. Both flavors return
|
||||
their last executed expression.</p>
|
||||
<p>The <code>when</code> keyword may be used in a given match arm to
|
||||
further restrict the conditions of execution, e.g.</p>
|
||||
<pre class="gp1"><code>con fs <- 43;
|
||||
|
||||
con is_even <- match fs {
|
||||
n when n % 2 == 0 => " is "
|
||||
_ => " is not "
|
||||
};
|
||||
|
||||
print(fs + is_even + "even.")</code></pre>
|
||||
<h3 id="loops">Loops</h3>
|
||||
<p>Several looping structures are supported in GP1</p>
|
||||
<ul>
|
||||
<li><code>loop</code></li>
|
||||
<li><code>for</code></li>
|
||||
<li><code>while</code></li>
|
||||
<li><code>do/while</code></li>
|
||||
</ul>
|
||||
<p>along with <code>continue</code> and <code>break</code> to help
|
||||
control program flow. All of these are statements.</p>
|
||||
<pre class="gp1"><code>loop { . . . } // an unconditional loop -- runs forever or until broken</code></pre>
|
||||
<pre class="gp1"><code>for i in some_iterable { . . . } // loop over anything that is iterable</code></pre>
|
||||
<pre class="gp1"><code>while some_bool { . . . } // classic conditional loop that executes until the predicate is false</code></pre>
|
||||
<pre class="gp1"><code>do { . . .
|
||||
} while some_bool // traditional do/while loop that ensures body executes at least once</code></pre>
|
||||
<h2 id="pattern-matching">Pattern Matching</h2>
|
||||
<p>Pattern matching behaves essentially as it does in SML, with support
|
||||
for various sorts of destructuring. It works in normal assignment and in
|
||||
<code>match</code> arms. It will eventually work in function parameter
|
||||
assignment, but perhaps not at first.</p>
|
||||
<p>For now, some examples.</p>
|
||||
<pre class="gp1"><code>a <- ("hello", "world"); // a is a tuple of strings
|
||||
(b, c) <- a;
|
||||
|
||||
assert(b == "hello" && c == "world")
|
||||
|
||||
fn u32_list_to_string(l: List<u32>): string { // this is assuming that square brackets are used for linked lists
|
||||
con elements <- match l {
|
||||
[] => "",
|
||||
[e] => string(e),
|
||||
h::t => string(h) + ", " + u32_list_to_string(t), // the bit before the arrow in each arm is a pattern
|
||||
} // h::t matches the head and tail of the list to h and t, respectively
|
||||
"[" + elements + "]" // [s] matches any single-element list
|
||||
} // [] matches any empty list</code></pre>
|
||||
<h2 id="interfaces">Interfaces</h2>
|
||||
<p>Interfaces are in Version 2 on the roadmap.</p>
|
||||
<h2 id="user-defined-types">User-Defined Types</h2>
|
||||
<h3 id="enums">Enums</h3>
|
||||
<p>Enums are pretty powerful in GP1. They can be the typical enumerated
|
||||
type you'd expect, like</p>
|
||||
<pre class="gp1"><code>enum Coin { penny, nickle, dime, quarter } // 'vanilla' enum
|
||||
|
||||
var a <- Coin.nickle
|
||||
assert a == Coin.nickle
|
||||
</code></pre>
|
||||
<p>Or an enum can have an implicit field named <code>value</code></p>
|
||||
<pre class="gp1"><code>enum Coin: u16 { penny(1), nickle(5), dime(10), quarter(25) }
|
||||
|
||||
var a <- Coin.nickle;
|
||||
assert(a == Coin.nickle);
|
||||
assert(a.value == 5);</code></pre>
|
||||
<p>Or an enum can be complex with a user-defined set of fields, like</p>
|
||||
<pre class="gp1"><code>enum CarModel(make: string, mass: f32, wheelbase: f32) { // enum with multiple fields
|
||||
gt ( "ford", 1581, 2.71018 ),
|
||||
c8_corvette ( "chevy", 1527, 2.72288 )
|
||||
}</code></pre>
|
||||
<p>A field can also have a function type. For example</p>
|
||||
<pre class="gp1"><code>enum CarModel(make: string, mass: f32, wheelbase: f32, gasUsage: fn<f32 | f32>) {
|
||||
gt ( "ford", 1581, 2.71018, { miles_traveled -> miles_traveled / 14 } ),
|
||||
c8_corvette ( "chevy", 1527, 2.72288, { miles_traveled -> miles_traveled / 19 } )
|
||||
}
|
||||
|
||||
var my_car <- CarModel.c8_corvette;
|
||||
var gas_used <- my_car.gasUsage(200); // estimate how much gas I'd use on a 200 mile trip</code></pre>
|
||||
<p>Equivalence of enums is not influenced by case values, e.g.</p>
|
||||
<pre class="gp1"><code>enum OneOrAnother: u16 { one(0), another(0) }
|
||||
|
||||
con a <- OneOrAnother.one;
|
||||
con b <- OneOrAnother.another;
|
||||
|
||||
assert(a != b);
|
||||
assert(a.value == b.value);</code></pre>
|
||||
<p>It's important to remember that enums are 100% always totally in
|
||||
every concieveable fashion immutable. To make this easier to enforce,
|
||||
only value types are allowed for enum fields.</p>
|
||||
<h3 id="records">Records</h3>
|
||||
<p>Records are record types, defined with the <code>record</code>
|
||||
keyword. Fields are defined in the <code>record</code> block and
|
||||
behavior is defined in the optional <code>impl</code> block.</p>
|
||||
<p>For example,</p>
|
||||
<pre class="gp1"><code>record Something {
|
||||
label: i32 // field label followed by some type
|
||||
} impl { . . . } // associated functions. This is different than having functions in the fields section because impl functions are not assignable.</code></pre>
|
||||
<p>If the record implements some interface, <code>SomeInterface</code>,
|
||||
the <code>impl</code> would be replaced with
|
||||
<code>impl SomeInterface</code>, and the functions of
|
||||
<code>SomeInterface</code> would be defined alongside any other
|
||||
functions of the <code>Something</code> record.</p>
|
||||
<h3 id="unions">Unions</h3>
|
||||
<p>Unions are the classic discriminated sum type.</p>
|
||||
<pre class="gp1"><code>union BinaryTree {
|
||||
Empty,
|
||||
Leaf: i32,
|
||||
Node: (BinaryTree BinaryTree),
|
||||
}</code></pre>
|
||||
<h3 id="type-aliases">Type Aliases</h3>
|
||||
<p>Refer to <em>Generics</em> for info on the syntax used in this
|
||||
section.</p>
|
||||
<p>Type aliasing is provided with the <code>type</code> keyword,
|
||||
e.g.</p>
|
||||
<pre class="gp1"><code>type TokenStream Sequence<Token>
|
||||
type Ast Tree<AbstractNode>
|
||||
|
||||
fn parse(ts: TokenStream): Ast { . . . }</code></pre>
|
||||
<p>Notice how much cleaner the function definition looks with the
|
||||
aliased types. This keyword is useful mainly for readability and domain
|
||||
modeling.</p>
|
||||
<h2 id="generics">Generics</h2>
|
||||
<p>Generics are in Version 2 on the official GP1 roadmap. They roughly
|
||||
use C++ template syntax or Rust generic syntax.</p>
|
||||
<h2 id="references-and-reference-types">References and Reference
|
||||
Types</h2>
|
||||
<p>GP1 has three operators involved in handling references,
|
||||
<code>#</code>, <code>&</code>, and <code>@</code>. These are
|
||||
immutable reference, mutable reference, and dereference, respectively.
|
||||
Some examples of referencing/dereferencing values:</p>
|
||||
<pre class="gp1"><code>var a <- "core dumped";
|
||||
var b <- &a; // b is a mutable reference to a
|
||||
|
||||
assert(a == @b);
|
||||
assert(a != b);
|
||||
|
||||
@b <- "missing ; at line 69, column 420";
|
||||
assert(a == "missing ; at line 69, column 420");
|
||||
|
||||
b <- &"missing ; at line 420, column 69";
|
||||
assert(a != "missing ; at line 420, column 69");
|
||||
|
||||
var c <- #b; // c is an immutable reference to b
|
||||
assert(@c == b);
|
||||
assert(@@c == a);
|
||||
|
||||
@c <- &"kablooey"; // this does not work. `c` is an immutable reference and cannot be used to assign its referent.</code></pre>
|
||||
<p>Naturally, only <code>var</code> values can be mutated through
|
||||
references.</p>
|
||||
<p>The reference operators may be prepended to any type, T, to describe
|
||||
the type of a reference to a value of type T, e.g.</p>
|
||||
<pre class="gp1"><code>fn set_through(ref: &string) { // this function takes a mutable reference to a string and returns `()`
|
||||
@ref <- "goodbye";
|
||||
}
|
||||
|
||||
var a <- "hello";
|
||||
set_through(&a);
|
||||
|
||||
assert(a == "goodbye");</code></pre>
|
1
acl.cool/site/assets/fonts
Symbolic link
1
acl.cool/site/assets/fonts
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../fonts
|
70
acl.cool/site/cats.ml
Normal file
70
acl.cool/site/cats.ml
Normal file
|
@ -0,0 +1,70 @@
|
|||
module type Functor = sig
|
||||
type 'a t
|
||||
val map : ('a -> 'b) -> 'a t -> 'b t
|
||||
end
|
||||
|
||||
module type Applicative = sig
|
||||
type 'a t
|
||||
val pure : 'a -> 'a t
|
||||
val apply : ('a -> 'b) t -> 'a t -> 'b t
|
||||
end
|
||||
|
||||
module type Monad = sig
|
||||
type 'a t
|
||||
val return : 'a -> 'a t
|
||||
val bind : ('a -> 'b t) -> 'a t -> 'b t
|
||||
end
|
||||
|
||||
module ApplicativeOfMonad (M : Monad) : Applicative with type 'a t = 'a M.t = struct
|
||||
type 'a t = 'a M.t
|
||||
let pure = M.return
|
||||
let apply f x = M.(bind (fun y -> bind (fun g -> return (g y)) f) x)
|
||||
end
|
||||
|
||||
module FunctorOfApplicative (A : Applicative) : Functor with type 'a t = 'a A.t = struct
|
||||
type 'a t = 'a A.t
|
||||
let map f x = A.(apply (pure f) x)
|
||||
end
|
||||
|
||||
module FunctorOfMonad (M : Monad) : Functor with type 'a t = 'a M.t = struct
|
||||
include FunctorOfApplicative(ApplicativeOfMonad(M))
|
||||
end
|
||||
|
||||
module MonadDerive (M : Monad) = struct
|
||||
include M
|
||||
include ApplicativeOfMonad(M)
|
||||
include FunctorOfMonad(M)
|
||||
let (>>=) x f = bind f x
|
||||
let (<$>) x f = map x f
|
||||
let (<*>) x f = apply x f
|
||||
end
|
||||
|
||||
module ListMonad = struct
|
||||
type 'a t = 'a list
|
||||
let return x = [x]
|
||||
let rec bind (f : 'a -> 'b list) : 'a list -> 'b list = function
|
||||
| [] -> []
|
||||
| x :: xs -> f x @ bind f xs
|
||||
end
|
||||
|
||||
module Dlm = MonadDerive(ListMonad)
|
||||
|
||||
let pair x y = x, y
|
||||
let cart_prod xs ys = Dlm.(pair <$> xs <*> ys)
|
||||
|
||||
let () = cart_prod [1;2;3;4] ["7"; "hello there"; "forthwith!"]
|
||||
|> List.iter (fun (x, y) -> print_endline @@ "(" ^ string_of_int x ^ ", " ^ y ^ ")")
|
||||
|
||||
|
||||
|
||||
(* ============================================================================================= *)
|
||||
|
||||
module StateMonad (S : sig type t end) = struct
|
||||
type 'a t = S.t -> S.t * 'a
|
||||
let return x s = (s, x)
|
||||
let bind f x s = let s', a = x s in f a s'
|
||||
end
|
||||
|
||||
module IntStateMonad = StateMonad(struct type t = int end)
|
||||
|
||||
|
70
acl.cool/site/cats.ml.txt
Normal file
70
acl.cool/site/cats.ml.txt
Normal file
|
@ -0,0 +1,70 @@
|
|||
module type Functor = sig
|
||||
type 'a t
|
||||
val map : ('a -> 'b) -> 'a t -> 'b t
|
||||
end
|
||||
|
||||
module type Applicative = sig
|
||||
type 'a t
|
||||
val pure : 'a -> 'a t
|
||||
val apply : ('a -> 'b) t -> 'a t -> 'b t
|
||||
end
|
||||
|
||||
module type Monad = sig
|
||||
type 'a t
|
||||
val return : 'a -> 'a t
|
||||
val bind : ('a -> 'b t) -> 'a t -> 'b t
|
||||
end
|
||||
|
||||
module ApplicativeOfMonad (M : Monad) : Applicative with type 'a t = 'a M.t = struct
|
||||
type 'a t = 'a M.t
|
||||
let pure = M.return
|
||||
let apply f x = M.(bind (fun y -> bind (fun g -> return (g y)) f) x)
|
||||
end
|
||||
|
||||
module FunctorOfApplicative (A : Applicative) : Functor with type 'a t = 'a A.t = struct
|
||||
type 'a t = 'a A.t
|
||||
let map f x = A.(apply (pure f) x)
|
||||
end
|
||||
|
||||
module FunctorOfMonad (M : Monad) : Functor with type 'a t = 'a M.t = struct
|
||||
include FunctorOfApplicative(ApplicativeOfMonad(M))
|
||||
end
|
||||
|
||||
module MonadDerive (M : Monad) = struct
|
||||
include M
|
||||
include ApplicativeOfMonad(M)
|
||||
include FunctorOfMonad(M)
|
||||
let (>>=) x f = bind f x
|
||||
let (<$>) x f = map x f
|
||||
let (<*>) x f = apply x f
|
||||
end
|
||||
|
||||
module ListMonad = struct
|
||||
type 'a t = 'a list
|
||||
let return x = [x]
|
||||
let rec bind (f : 'a -> 'b list) : 'a list -> 'b list = function
|
||||
| [] -> []
|
||||
| x :: xs -> f x @ bind f xs
|
||||
end
|
||||
|
||||
module Dlm = MonadDerive(ListMonad)
|
||||
|
||||
let pair x y = x, y
|
||||
let cart_prod xs ys = Dlm.(pair <$> xs <*> ys)
|
||||
|
||||
let () = cart_prod [1;2;3;4] ["7"; "hello there"; "forthwith!"]
|
||||
|> List.iter (fun (x, y) -> print_endline @@ "(" ^ string_of_int x ^ ", " ^ y ^ ")")
|
||||
|
||||
|
||||
|
||||
(* ============================================================================================= *)
|
||||
|
||||
module StateMonad (S : sig type t end) = struct
|
||||
type 'a t = S.t -> S.t * 'a
|
||||
let return x s = (s, x)
|
||||
let bind f x s = let s', a = x s in f a s'
|
||||
end
|
||||
|
||||
module IntStateMonad = StateMonad(struct type t = int end)
|
||||
|
||||
|
1
acl.cool/site/css
Symbolic link
1
acl.cool/site/css
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../css
|
BIN
acl.cool/site/culture.dot.png
Normal file
BIN
acl.cool/site/culture.dot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 113 KiB |
114
acl.cool/site/culture.dot.svg
Normal file
114
acl.cool/site/culture.dot.svg
Normal file
|
@ -0,0 +1,114 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 12.1.0 (20240811.2233)
|
||||
-->
|
||||
<!-- Pages: 1 -->
|
||||
<svg width="844pt" height="426pt"
|
||||
viewBox="0.00 0.00 843.85 425.92" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 421.92)">
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-421.92 839.85,-421.92 839.85,4 -4,4"/>
|
||||
<!-- cp -->
|
||||
<g id="node1" class="node">
|
||||
<title>cp</title>
|
||||
<ellipse fill="#cceedd" stroke="#aaaaaa" cx="102.53" cy="-375" rx="96.17" ry="19.45"/>
|
||||
<text text-anchor="start" x="42.53" y="-372.45" font-family="Open Sans" font-weight="bold" font-size="14.00">Consider Phlebas</text>
|
||||
</g>
|
||||
<!-- ltw -->
|
||||
<g id="node7" class="node">
|
||||
<title>ltw</title>
|
||||
<ellipse fill="#cceedd" stroke="#aaaaaa" cx="102.53" cy="-209" rx="102.53" ry="19.45"/>
|
||||
<text text-anchor="start" x="38.03" y="-206.45" font-family="Open Sans" font-weight="bold" font-size="14.00">Look to Windward</text>
|
||||
</g>
|
||||
<!-- cp->ltw -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>cp->ltw</title>
|
||||
<path fill="none" stroke="black" d="M102.53,-355.19C102.53,-327.09 102.53,-273.87 102.53,-240.26"/>
|
||||
<polygon fill="black" stroke="black" points="106.03,-240.36 102.53,-230.36 99.03,-240.36 106.03,-240.36"/>
|
||||
</g>
|
||||
<!-- pog -->
|
||||
<g id="node2" class="node">
|
||||
<title>pog</title>
|
||||
<ellipse fill="#cceedd" stroke="#aaaaaa" cx="392.53" cy="-375" rx="112.08" ry="19.45"/>
|
||||
<text text-anchor="start" x="321.28" y="-372.45" font-family="Open Sans" font-weight="bold" font-size="14.00">The Player of Games</text>
|
||||
</g>
|
||||
<!-- uow -->
|
||||
<g id="node3" class="node">
|
||||
<title>uow</title>
|
||||
<ellipse fill="#cceedd" stroke="#aaaaaa" cx="312.53" cy="-209" rx="89.8" ry="19.45"/>
|
||||
<text text-anchor="start" x="257.03" y="-206.45" font-family="Open Sans" font-weight="bold" font-size="14.00">Use of Weapons</text>
|
||||
</g>
|
||||
<!-- sota -->
|
||||
<g id="node4" class="node">
|
||||
<title>sota</title>
|
||||
<ellipse fill="#cceedd" stroke="#aaaaaa" cx="311.53" cy="-43" rx="107.3" ry="19.45"/>
|
||||
<text text-anchor="start" x="243.66" y="-40.45" font-family="Open Sans" font-weight="bold" font-size="14.00">The State of the Art</text>
|
||||
</g>
|
||||
<!-- uow->sota -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>uow->sota</title>
|
||||
<path fill="none" stroke="black" d="M312.42,-189.19C312.24,-161.09 311.92,-107.87 311.71,-74.26"/>
|
||||
<polygon fill="black" stroke="black" points="315.22,-74.34 311.65,-64.36 308.22,-74.38 315.22,-74.34"/>
|
||||
</g>
|
||||
<!-- ivs -->
|
||||
<g id="node6" class="node">
|
||||
<title>ivs</title>
|
||||
<ellipse fill="#cceedd" stroke="#aaaaaa" cx="499.53" cy="-43" rx="62.23" ry="19.45"/>
|
||||
<text text-anchor="start" x="463.53" y="-40.45" font-family="Open Sans" font-weight="bold" font-size="14.00">Inversions</text>
|
||||
</g>
|
||||
<!-- uow->ivs -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>uow->ivs</title>
|
||||
<path fill="none" stroke="black" d="M333.26,-189.82C366.71,-160.48 432.69,-102.62 470.71,-69.28"/>
|
||||
<polygon fill="black" stroke="black" points="472.93,-71.98 478.14,-62.76 468.32,-66.72 472.93,-71.98"/>
|
||||
</g>
|
||||
<!-- sd -->
|
||||
<g id="node9" class="node">
|
||||
<title>sd</title>
|
||||
<ellipse fill="#cceedd" stroke="#aaaaaa" cx="104.53" cy="-43" rx="80.26" ry="19.45"/>
|
||||
<text text-anchor="start" x="55.78" y="-40.45" font-family="Open Sans" font-weight="bold" font-size="14.00">Surface Detail</text>
|
||||
</g>
|
||||
<!-- uow->sd -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>uow->sd</title>
|
||||
<path fill="none" stroke="black" d="M289.48,-189.82C252.21,-160.44 178.64,-102.44 136.39,-69.12"/>
|
||||
<polygon fill="black" stroke="black" points="138.58,-66.39 128.56,-62.95 134.25,-71.89 138.58,-66.39"/>
|
||||
</g>
|
||||
<!-- ex -->
|
||||
<g id="node5" class="node">
|
||||
<title>ex</title>
|
||||
<ellipse fill="#cceedd" stroke="#aaaaaa" cx="613.53" cy="-375" rx="58.51" ry="19.45"/>
|
||||
<text text-anchor="start" x="580.16" y="-372.45" font-family="Open Sans" font-weight="bold" font-size="14.00">Excession</text>
|
||||
</g>
|
||||
<!-- mat -->
|
||||
<g id="node8" class="node">
|
||||
<title>mat</title>
|
||||
<ellipse fill="#cceedd" stroke="#aaaaaa" cx="539.53" cy="-209" rx="45.25" ry="19.45"/>
|
||||
<text text-anchor="start" x="515.53" y="-206.45" font-family="Open Sans" font-weight="bold" font-size="14.00">Matter</text>
|
||||
</g>
|
||||
<!-- ex->mat -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>ex->mat</title>
|
||||
<path fill="none" stroke="black" d="M605.19,-355.51C592.29,-326.93 567.44,-271.85 552.28,-238.27"/>
|
||||
<polygon fill="black" stroke="black" points="555.66,-237.23 548.35,-229.56 549.28,-240.11 555.66,-237.23"/>
|
||||
</g>
|
||||
<!-- hs -->
|
||||
<g id="node10" class="node">
|
||||
<title>hs</title>
|
||||
<ellipse fill="#cceedd" stroke="#aaaaaa" cx="719.53" cy="-209" rx="116.32" ry="19.45"/>
|
||||
<text text-anchor="start" x="645.28" y="-206.45" font-family="Open Sans" font-weight="bold" font-size="14.00">The Hydrogen Sonata</text>
|
||||
</g>
|
||||
<!-- ex->hs -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>ex->hs</title>
|
||||
<path fill="none" stroke="black" d="M625.48,-355.51C644.01,-326.84 679.79,-271.49 701.48,-237.93"/>
|
||||
<polygon fill="black" stroke="black" points="704.15,-240.25 706.64,-229.95 698.27,-236.45 704.15,-240.25"/>
|
||||
</g>
|
||||
<!-- ltw->sd -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>ltw->sd</title>
|
||||
<path fill="none" stroke="black" d="M102.76,-189.19C103.1,-161.09 103.75,-107.87 104.16,-74.26"/>
|
||||
<polygon fill="black" stroke="black" points="107.66,-74.4 104.28,-64.36 100.66,-74.32 107.66,-74.4"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.3 KiB |
21
acl.cool/site/culture.dot.txt
Normal file
21
acl.cool/site/culture.dot.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
digraph {
|
||||
node [style=filled, fontname="Open Sans", fillcolor="#cceeddff", color="#aaaaaaff"];
|
||||
ratio=0.5;
|
||||
cp [label=<<B>Consider Phlebas</B>>];
|
||||
pog [label=<<B>The Player of Games</B>>];
|
||||
uow [label=<<B>Use of Weapons</B>>];
|
||||
sota [label=<<B>The State of the Art</B>>];
|
||||
ex [label=<<B>Excession</B>>];
|
||||
ivs [label=<<B>Inversions</B>>];
|
||||
ltw [label=<<B>Look to Windward</B>>];
|
||||
mat [label=<<B>Matter</B>>];
|
||||
sd [label=<<B>Surface Detail</B>>];
|
||||
hs [label=<<B>The Hydrogen Sonata</B>>];
|
||||
cp -> ltw; // It's about the Idiran War, dummy.
|
||||
uow -> sota; // It's largely about Sma.
|
||||
uow -> ivs; // This book gives the best idea about what SC is, and you should know that before reading Inversions.
|
||||
ex -> hs; // Hydrogen Sonata is dual to Excession in many ways. Not a hard rule, but HS is better if you know Excession.
|
||||
ex -> mat; // Sleeper Service mentioned as "The granddaddy, the exemplary hero figure, the very God...".
|
||||
uow -> sd; // Zakalwe/chair killer is in this one, and you should know who that is.
|
||||
ltw -> sd; // LTW is more impactful (with Chel heaven being specially real) without the knowledge of SD.
|
||||
}
|
5
acl.cool/site/index.html
Normal file
5
acl.cool/site/index.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<h1><a href="/resume.pdf" style="text-decoration: none;">acl</a>.cool</h1>
|
||||
|
||||
<div id="writings" style="color: var(--lighter-color); line-height: var(--ui-spacing);"></span>
|
||||
|
||||
<p><strong>Welcome! Below are links to things I've made or just enjoy.</strong></p>
|
BIN
acl.cool/site/msvcr110.dll
Normal file
BIN
acl.cool/site/msvcr110.dll
Normal file
Binary file not shown.
BIN
acl.cool/site/resume.pdf
Executable file
BIN
acl.cool/site/resume.pdf
Executable file
Binary file not shown.
112
acl.cool/site/resume.typ
Executable file
112
acl.cool/site/resume.typ
Executable file
|
@ -0,0 +1,112 @@
|
|||
#let fontsize = 10.2pt
|
||||
#let typeface_text = "Fira Sans"
|
||||
#let typeface_math = "STIX Two Math"
|
||||
|
||||
#set text(font: typeface_text, size: fontsize)
|
||||
#show math.equation: set text(font: typeface_math, size: fontsize)
|
||||
|
||||
#let inlineFrac(a, b) = [$#super([#a]) #h(-1pt) slash #h(-1pt) #sub([#b])$]
|
||||
|
||||
#set smartquote(enabled: false)
|
||||
|
||||
#show heading: q => {
|
||||
if q.depth != 1 {
|
||||
v(fontsize / 5)
|
||||
}
|
||||
text(q, weight: "bold")
|
||||
if q.depth == 2 {
|
||||
v(-0.7em)
|
||||
line(length: 100%, stroke: (thickness: 0.7pt))
|
||||
v(0.5em)
|
||||
}
|
||||
}
|
||||
|
||||
#let head = {
|
||||
align(center)[
|
||||
= Alexander Lucas
|
||||
#set text(font: "Fira Code")
|
||||
#text([alexander.clay.lucas\@gmail.com], size: fontsize * 0.9)
|
||||
#linebreak()
|
||||
#text([(+1) 347-644-9265], size: fontsize * 0.8)
|
||||
]
|
||||
}
|
||||
|
||||
#set page(margin: (x: 0.9in, y: 0.35in))
|
||||
|
||||
#head
|
||||
|
||||
== Summary
|
||||
I am a computer science enthusiast with an inclination for harnessing computer science
|
||||
theory to tackle practical challenges as cleanly as possible. I believe strongly in the
|
||||
importance of codebase health and quality, maintaining those values over time as programs and projects evolve.
|
||||
|
||||
== Skills
|
||||
#let skills = [
|
||||
*Languages*: Javascript/Typescript, Java, Python, Rust, Haskell, OCaml, F\#, Ruby, C\#, C, C++, Lean 4, HTML/CSS, LaTeX, Typst
|
||||
#linebreak()
|
||||
*Platforms*: Ten years using GNU/Linux including Debian and Redhat, QEMU, Google Cloud
|
||||
#linebreak()
|
||||
*Technologies*: Buildroot, WebGL, Numpy/Pytorch/Sklearn, Matplotlib, Git, Gitlab/Github, PostgreSQL, Node, Slurm
|
||||
#linebreak()
|
||||
*Soft Skills*: Technical Writing, Software Documentation, Presentation
|
||||
]
|
||||
|
||||
#skills
|
||||
|
||||
#let interline() = {
|
||||
box(width: 1fr, inset: fontsize / 4, line(length: 100%, stroke: (thickness: fontsize / 10, dash: "loosely-dotted")))
|
||||
}
|
||||
|
||||
== Experience
|
||||
#let experience = [
|
||||
*Embedded Software Engineer, Jr.* #interline() #text(weight: "semibold")[Trusted Microelectronics, KBR, 01/2025-05/2025 (End of Funds)]
|
||||
- Continuing to work with the same great team, tools and software as during my internship.
|
||||
- Developing QEMU virtual hardware devices for building/testing platform-specific applications.
|
||||
|
||||
*Linux Driver Development Intern* #interline() #text(weight: "semibold")[Trusted Microelectronics, KBR, 05/2024-08/2024]
|
||||
- Learned Linux kernel subsystems and developed device drivers for custom "system on a chip" hardware, including GPIO/pin controllers and an AES encryption accelerator module.
|
||||
- Worked with team members to develop testing and assurance methodologies including coverage profiling and input fuzzing for Linux drivers while porting Linux to our boards.
|
||||
- Automated common tasks, writing scripts to handle OS installations and code restructuring.
|
||||
- Presented project status and details to large, cross-functional and interdisciplinary groups.
|
||||
|
||||
*Teaching Assistant* #interline() #text(weight: "semibold")[James Madison University, 08/2022-12/2023]
|
||||
- Took questions and led review sessions in proofs, programming, tooling, debugging code.
|
||||
- Maintained a calm and encouraging environment while helping students with difficult problem sets against a deadline.
|
||||
]
|
||||
|
||||
#experience
|
||||
|
||||
== Education
|
||||
#let degrees = [
|
||||
*B.S. Computer Science* (3.8 GPA) #interline() #text(weight: "semibold")[James Madison University, 12/2023]
|
||||
]
|
||||
#degrees
|
||||
#let courses = [
|
||||
- Programming Languages, Compiler Construction
|
||||
- Independent Study in Constructive Logic, Symbolic Logic
|
||||
- Applied Algorithms, Data Structures
|
||||
- Parallel and Distributed Systems, 3D Graphics
|
||||
]
|
||||
#courses
|
||||
*Study Abroad, London, UK* #interline() #text(weight: "semibold")[JMU at Florida State Study Center, Summer 2023]
|
||||
#let cw = [
|
||||
- Rigidity Theory
|
||||
- Independent Study in Computational Geometry
|
||||
]
|
||||
#cw
|
||||
|
||||
*Academic Awards*
|
||||
#let awards = [
|
||||
- "President's List" #interline() #text(weight: "semibold")[JMU, 2023]
|
||||
- "Alonzo Church Award for Theory" #interline() #text(weight: "semibold")[JMU CS Department, 2024]
|
||||
]
|
||||
#awards
|
||||
|
||||
== Personal Projects
|
||||
#let projects = [
|
||||
*Aasam* (on #underline([#link("https://hackage.haskell.org/package/aasam")[Hackage]])) is a Haskell implementation of the CFG-generation algorithm $#math.cal([M])$ from Annika Aasa's paper "Precedences in specifications and implementations of programming languages".
|
||||
#linebreak()
|
||||
*Randall* (on #underline([#link("https://gitlab.com/mobotsar/randall")[Gitlab]])) is a Discord bot for executing dice-notation, making it easy to play TTRPGs remotely. It uses a recursive descent parser and tree-walk interpreter on the backend and the .NET Discord library up front.
|
||||
]
|
||||
|
||||
#projects
|
42
acl.cool/site/writings/amar.dj
Normal file
42
acl.cool/site/writings/amar.dj
Normal file
|
@ -0,0 +1,42 @@
|
|||
# Amarettis (Chewy Almond Cookies)
|
||||
|
||||
These are genuinely excellent and surprisingly undemanding to make, particularly if you don't beat the egg whites by hand. From start to finish, the process should take less than an hour.
|
||||
|
||||
Keep in mind that the ideal crispy-outside chewy-inside texture forms over time while the cookies are cool. They _taste_ right immediately after coming out of the oven, but for the best texture, let them cool completely and rest in a sealed container for several hours before consuming.
|
||||
|
||||
Total caloric content of this recipe is 2840 kilocalories, or 109.6 kcal per cookie.
|
||||
|
||||
## Ingredients
|
||||
|
||||
- 1/2 cup white granulated sugar
|
||||
- 1/2 cup demerara sugar (Florida Crystals or similar)
|
||||
- 280 grams blanched almond flour
|
||||
- 1/2 teaspoon kosher salt
|
||||
- 3 extra large eggs
|
||||
- 1/2 teaspoon vanilla extract
|
||||
- 1 ounce (weight) Lazzaroni Amaretto
|
||||
- 1 cup powdered sugar
|
||||
- 25 whole, roasted almonds
|
||||
|
||||
## Equipment
|
||||
|
||||
- A medium mixing bowl (for dry ingredients)
|
||||
- A small mixing bowl (for beating egg whites)
|
||||
- A whisk or mixer
|
||||
- A standard set of measuring spoons
|
||||
- A kitchen scale
|
||||
- A medium cookie sheet
|
||||
- A silicone sheet-pan liner
|
||||
|
||||
## Process
|
||||
|
||||
1. Preheat the oven to 325°F.
|
||||
2. Mix the flour and sugars into a medium bowl.
|
||||
3. Separate three egg whites into another bowl and discard the yolks.
|
||||
4. Beat the egg whites until peaks are stiff.
|
||||
5. Add the vanilla extract and amaretto to the bowl of dry ingredients.
|
||||
6. Add the beaten egg whites to the dries and fold gently until a paste forms.
|
||||
7. Form small balls of the dough and coat them completely in powdered sugar.
|
||||
8. Gently press the dough balls onto a sheet-pan with a silicone mat, flattening them slightly.
|
||||
9. Press an almond into the top of each cookie so that the dough will hold it when baked.
|
||||
10. Place the pan (with cookies) in the oven for 25 minutes then remove it and let them cool.
|
17
acl.cool/site/writings/assam.dj.draft
Normal file
17
acl.cool/site/writings/assam.dj.draft
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Algorithm ℳ
|
||||
|
||||
During the implementation of my library "Aasam", based on the paper "Precedences in specifications and implementations of programming languages" by Annika Aasa, was the first time I fully read and understood an academic CS paper. It's a nice algorithm, and worth revisiting.
|
||||
|
||||
If you want to look at the implementation ahead-of-time, [Hackage](https://hackage.haskell.org/package/aasam) has got you covered. Frankly though, just keep reading; it was also the first time I wrote a Haskell program, and the code not terribly penetrable.
|
||||
|
||||
## Distfix Grammars
|
||||
|
||||
_Distfix grammars and notation are more commonly referred to as "mixfix", but the paper calls them "distfix" and that's what I'm sticking with._
|
||||
|
||||
The idea of distfix grammars is to provide a formalism for manipulating user-defined operators. The formalism is weaker than that of context-free grammars --- CFGs can describe far more languages then DFGs --- but it is because of this weakness that we can reason about DFGs with relative ease.
|
||||
|
||||
Let's look at a definition.
|
||||
|
||||
*STUFF HERE*
|
||||
|
||||
As we can see, ...
|
29
acl.cool/site/writings/chili.dj
Normal file
29
acl.cool/site/writings/chili.dj
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Unassailable Slow-Cooker Chili
|
||||
|
||||
This is a simple recipe of beans, tomato, and ground beef, refined across generations into the local maximum you see before you.
|
||||
|
||||
## Ingredients
|
||||
|
||||
- A bit more than 1 lb of ground beef
|
||||
- 2 14.5 oz cans of diced tomatoes
|
||||
- 2 1.25 oz packets of chili powder spice mix (such as McCormick, etc)
|
||||
- 4 cans of beans (typically one each of pinto, black, great-northern, and light kidney)
|
||||
- 1 large bottle of tomato juice
|
||||
- 1 medium or large onion
|
||||
- Black pepper to taste
|
||||
|
||||
## Equipment
|
||||
|
||||
- A knife to cut the onion
|
||||
- A skillet to cook the beef
|
||||
- A large slow-cooker
|
||||
- A heat-resistant spoon
|
||||
|
||||
## Process
|
||||
|
||||
1. Cook the ground beef until most of the fat has rendered, then drain most of the grease away.
|
||||
2. Chop onion and add to skillet with beef, cooking just until color develops.
|
||||
3. Add the beef and onion to the pot with tomatoes, beans, and spice mix.
|
||||
5. Pour in tomato juice until it reaches a consistency too thick for soup but still suitable for consumption with a spoon more than with any other dining implement.
|
||||
4. Cook on low until you're happy with the texture of your onions, as these usually take the longest.
|
||||
5. Black pepper to taste shortly before serving.
|
45
acl.cool/site/writings/crown.dj
Normal file
45
acl.cool/site/writings/crown.dj
Normal file
|
@ -0,0 +1,45 @@
|
|||
# Typical Coronation Chicken
|
||||
|
||||
This recipe is adapted from the original recipe used for Queen Elizabeth's “Coronation Luncheon” in 1953 and faithfully incorporates elements of several variations served around London in 2023. Most of the changes I've made are to ratios, but I've also included more fruits, omitted watercress, and used a mayonnaise/milk combination in place of whipping cream.
|
||||
|
||||
## Ingredients
|
||||
|
||||
- 3 tbsp almond slivers
|
||||
- 1/2 shallot
|
||||
- 1 tbsp dried apricots
|
||||
- 1 tbsp lemon juice (roughly 1/4 of a smooth lemon)
|
||||
- 1 tbsp extra-virgin olive oil
|
||||
- 3 tsp your favorite yellow curry powder
|
||||
- 1 tsp tomato paste
|
||||
- 90 ml dry red wine
|
||||
- 30 ml water
|
||||
- 1/4 tsp dark brown sugar
|
||||
- 225 ml Duke's mayonnaise \*
|
||||
- 100 ml 2% milk \*
|
||||
- 4 tsp thompson raisins \*
|
||||
- 1 tsp dried black currants \*
|
||||
- 650 g shredded cooked chicken breast *
|
||||
|
||||
Values marked with \* have been estimated after-the-fact. I made visual judgments during preparation to decide actual amounts and neglected to record them. The combined volume of mayonnaise and milk was measured at 325 ml. The chicken was measured at 500 g, but was too little for the amount of dressing made.
|
||||
|
||||
## Equipment
|
||||
|
||||
- A wide skillet or frypan
|
||||
- A knife and cutting surfaces
|
||||
- A standard set of measuring spoons
|
||||
- Two medium mixing bowls
|
||||
|
||||
## Process
|
||||
|
||||
1. Toast the almonds in your pan before setting them aside.
|
||||
2. Chop the half shallot and apricots very finely.
|
||||
3. Squeeze 1 tbsp of lemon juice and set it aside.
|
||||
4. Add olive oil to the pan and place it on medium heat.
|
||||
5. Add the shallot and curry powder then cook about two minutes or until the shallot begins to soften.
|
||||
6. Add tomato paste, wine, and water, then bring the pan to a mild boil.
|
||||
7. Once it boils, add lemon juice and brown sugar then simmer until the mixture is slightly reduced.
|
||||
8. Remove from heat and let cool substantially.
|
||||
9. Transfer to a bowl and mix in mayonnaise, milk, almonds, and all the fruit to complete the dressing.
|
||||
10. Place shredded chicken in another bowl and add dressing until the desired consistency is reached.
|
||||
11. Sample the mixture before adding salt and pepper to taste.
|
||||
12. Let rest in the refrigerator until completely cooled.
|
17
acl.cool/site/writings/culture.dj
Normal file
17
acl.cool/site/writings/culture.dj
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Reading Order of The Culture
|
||||
|
||||
I've generated a dependency graph for the Culture series reading order. The idea is that if there's an arrow from book A to book B, then to get the most possible enjoyment from either A or B, A should be read before B.
|
||||
|
||||
 Above is the graph, and [right here](/culture.dot.txt) is the vizgraph description file that lists my rationale for each dependency.
|
||||
|
||||
- _Consider Phlebas_ before _Look to Windward_--- both are about the Idiran War.
|
||||
- _Use of Weapons_ before _The State of the Art_--- these share a main character in Diziet Sma. SotA was actually released before UoW but is more satisfying if read after it, in my opinion (and the author's, for what that's worth).
|
||||
- _Use of Weapons_ before _Inversions_--- UoW gives the best idea of any book about what Special Circumstances is, which must be understood to fully appreciate Inversions in all its subtlety.
|
||||
- _Excession_ before _The Hydrogen Sonata_--- Hydrogen Sonata is dual to Excession in many ways that can't be explained here without abject spoilage. This one is not a hard rule, but HS is better if you know Excession.
|
||||
- _Excession_ before _Matter_--- GSV Sleeper Service is mentioned in Matter as "The granddaddy, the exemplary hero figure, the very God...", referencing events in Excession.
|
||||
- _Use of Weapons_ before _Surface Detail_--- you must know who Zakalwe is, the main character of UoW, to fully appreciate the ending of Surface Detail.
|
||||
- _Look to Windward_ before _Surface Detail_--- These book deal with some common themes and subjects. Some will disagree with me here, but LtW is more impactful _without_ certain knowledge revealed in Surface Detail.
|
||||
|
||||
Assuming one agrees with the graph, the set of ideal reading orders (that is, the set such that for all orders it contains, no order exists which is strictly better) is the set of [topological sorts](https://en.wikipedia.org/wiki/Topological_sortinghttps://en.wikipedia.org/wiki/Topological_sorting) of the graph.
|
||||
|
||||
This gives the number of possible ideal orders as 63840. That's a lot of good ways to do it!
|
3
acl.cool/site/writings/nomad.dj.draft
Normal file
3
acl.cool/site/writings/nomad.dj.draft
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Living Free with Monads
|
||||
|
||||
Explicit record passing makes OCaml feel _good_.
|
Loading…
Add table
Add a link
Reference in a new issue