From 1045ede124fc60de41f273d7997521d386c82bf1 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Sun, 24 Jul 2022 16:04:24 -0400 Subject: Editing pass over first two tutorials --- docs/tutorial/expression.html | 25 +++++++++++++------------ docs/tutorial/list.html | 23 ++++++++++++----------- 2 files changed, 25 insertions(+), 23 deletions(-) (limited to 'docs/tutorial') diff --git a/docs/tutorial/expression.html b/docs/tutorial/expression.html index 3708dc95..9ce866a7 100644 --- a/docs/tutorial/expression.html +++ b/docs/tutorial/expression.html @@ -119,7 +119,7 @@ ↗️
    (4÷3) × π × 23
 33.51032163829112
 
-

The evaluation order is diagrammed below, with the function on the first line evaluated first, then × on the next, and so on. The effect of the parentheses is that ÷ is evaluated before the leftmost ×.

+

The evaluation order is diagrammed below, with the function at the highest level evaluated first, then × below it, and so on. The effect of the parentheses is that ÷ is evaluated before the leftmost ×.

@@ -223,21 +223,21 @@ -

But wait: how do we know that in the expressions above uses the one-argument form? Remember that it can also take a left argument. For that matter, how do we know that - takes two arguments and not just one? Maybe this looks trivial right now: a good enough answer while we're only doing arithmetic is that a function is called with one argument if there is nothing to its left, or another function, and with two arguments otherwise. But it will get more complicated as we expand the syntax with expressions that can return functions and so on, so it's a good idea to discuss the foundations that will allow us to handle that complexity. In BQN, the way expressions are evaluated—the sequence of function calls and other operations—is determined by the syntactic role of the things it contains. A few rules of roles make sense of the syntax seen so far:

+

But wait: how do we know that in the expressions above uses the one-argument form? Remember that it can also take a left argument. For that matter, how do we know that - takes two arguments and not just one? Maybe this looks trivial right now: a good enough answer while we're only doing arithmetic is that a function is called with one argument if there is nothing to its left, or another function, and with two arguments otherwise. But it'll get more complicated as we expand the syntax with expressions that can return functions and so on, so it's a good idea to discuss the foundations that will allow us to handle that complexity. In BQN, the way expressions are evaluated—the sequence of function calls and other operations—is determined by the syntactic role of the things it contains. A few rules of roles make sense of the syntax seen so far:

-

Perhaps more than you thought! To really get roles, it's important to understand that a role is properly a property of an expression, and not its value. In language implementation terms, roles are used only to parse expressions, giving a syntax tree, but don't dictate what values are possible when the tree is evaluated. So it's possible to have a function with a number role or a number with a function role. The reason this doesn't happen with the numeric literals and primitives we've introduced is that these tokens have a constant value. × or have the same value in any possible program, and so it makes sense that their types and roles should correspond. When we introduce identifiers, we'll see this correspondence break down—and why that's good!

+

Perhaps more than you thought! To really get roles, it's important to understand that a role is properly a property of an expression, and not its value. In language implementation terms, roles are used only to parse expressions, giving a syntax tree, but don't dictate what values may appear when the tree is evaluated. So it's possible to have a function with a number role or a number with a function role. The reason this doesn't happen with the numeric literals and primitives we've introduced is that these tokens have a constant value. × or have the same value in any possible program, and so it makes sense that their types and roles should correspond. When we introduce identifiers, we'll see this correspondence break down—and why that's good!

Character arithmetic

Gosh, that's a lot of arithmetic up there. Maybe adding characters will mix things up a bit? Hang on, you can't add characters, only subtract them… let's back up.

↗️
    'c'
 'c'
 
-

A character is written with single quotes. As in the C language, it's not the same as a string, which is written with double quotes. There are no escapes for characters: any source code character between single quotes becomes a character literal, even a newline. Some kinds of arithmetic extend to characters:

+

A character is written with single quotes. As in the C language, it's not the same as a string, which is written with double quotes. There are no character escapes: any source code character between single quotes becomes a character literal, even a newline. Some kinds of arithmetic extend to characters:

↗️
    'c' + 1
 'd'
     'h' - 'a'
@@ -285,7 +285,7 @@
 

It's a convenient way to write non-printing characters without having to include them in your source code: for example @+10 is the newline character.

Addition and subtraction with affine characters have all the same algebraic properties that they do with numbers. One way to see this is to think of values as a combination of "characterness" (0 for numbers and 1 for characters) and either numeric value or code point. Addition and subtraction are done element-wise on these pairs of numbers, and are allowed if the result is a valid value, that is, its characterness is 0 or 1 and its value is a valid code point if the characterness is 1. However, because the space of values is no longer closed under addition and subtraction, certain rearrangements of valid computations might not work, if one of the values produced in the middle isn't legal.

Modifiers

-

Functions are nice and all, but to really bring us into the space age BQN has a second level of function called modifiers (the space age in this case is when operators were introduced to APL in the early 60s—hey, did you know the second APL conference was held at Goddard Space Flight Center?). While functions apply to subjects, modifiers can apply to functions or subjects, and return functions. For example, the 1-modifier ˜ modifies one function—that's where the 1 comes from—by swapping the arguments before calling it (Swap), or copying the right argument to the left if there's only one (Self).

+

Functions are nice and all, but to really bring us into the space age BQN has a second level of function called modifiers (the space age in this case is when operators were introduced to APL in the early 60s—hey, did you know the second APL conference was held at Goddard Space Flight Center?). While functions apply to subjects, modifiers apply to functions or subjects, and return functions. For example, the 1-modifier ˜ modifies one function—that's where the 1 comes from—by swapping the arguments before calling it (Swap), or copying the right argument to the left if there's only one (Self).

↗️
    2 -˜ 'd'  # Subtract from
 'b'
     +˜ 3      # Add to itself
@@ -317,13 +317,14 @@
   
 
 
-

Another 1-modifier is Undo (). BQN has just enough computer algebra facilities to look like a tool for Neanderthals next to a real computer algebra system, and among them is the ability to invert some primitives. In general you can't be sure when Undo will work (it might even be undecidable), but the examples I'll give here are guaranteed by the spec to always work in the same way. Starting with a third way to square a number:

+

Another 1-modifier is Undo (). BQN has just enough computer algebra facilities to look like a tool for Neanderthals next to a real computer algebra system, and among them is the ability to invert some primitives. Undo has a specification that fixes results for some functions and modifiers, but allows implementations to go further. In the tutorials we'll stick to the required stuff, which is definitely enough to be practically useful. Starting with a third way to square a number:

↗️
     4
 16
 

But the most important use for Undo in arithmetic is the logarithm, written . That's all a logarithm is: it undoes the Power function! With no left argument is the natural logarithm. If there's a left argument then Undo considers it part of the function to be undone. The result in this case is that with two arguments is the logarithm of the right argument with base given by the left one.

-↗️
     10
+↗️
     10
 2.302585092994046
+
     2  32    # Log base 2
 5
     2  2  32
@@ -331,14 +332,14 @@
     10  1e4  # Log base 10 of a number in scientific notation
 4
 
-

Another 1-modifier, which at the moment is tremendously useless, is Constant ˙. It turns its operand into a constant function that always returns the operand (inputs to modifiers are called operands because modificands is just too horrible).

+

Another 1-modifier, which at the moment is tremendously useless, is Constant (˙). It turns its operand into a constant function that always returns the operand (inputs to modifiers are called operands because modificands is just too horrible).

↗️
    2 3˙ 4
 3
 

Well, I guess it's not pedagogically useless, as it does demonstrate that a modifier can be applied to subjects as well as functions. Even though 3 is a subject, 3˙ is a function, and can be applied to and ignore the two arguments 2 and 4.

With three examples you may have noticed that 1-modifiers tend to cluster at the top of the line. In fact, every primitive 1-modifer is a superscript character: we've covered ˜⁼˙, and the remaining array-based modifiers ˘¨⌜´˝` will show up later.

2-modifiers

-

Made it to the last role, the 2-modifier (if you think something's been skipped, you're free to call subjects 0-modifiers. They don't modify anything. Just not when other people can hear you). To introduce them we'll use Atop , which composes two functions as in mathematics. The resulting function allows one or two arguments like any BQN function: these are all passed to the function on the right, and the result of that application is passed to the function on the left. So the function on the left is only ever called with one argument.

+

Made it to the last role, the 2-modifier (if you think something's been skipped, you're free to call subjects 0-modifiers. They don't modify anything. Just not when other people can hear you). To introduce them we'll use Atop (), which composes two functions as in mathematics. The resulting function allows one or two arguments like any BQN function: these are all passed to the function on the right, and the result of that application is passed to the function on the left. So the function on the left is only ever called with one argument.

↗️
    3 ט+ 4  # Square of 3 plus 4
 49
     -(ט) 5  # Negative square of 5
@@ -387,8 +388,8 @@
 
 
 

This ordering is more consistent with the rule that a 1-modifier's operand should go to its left. If we tried going from right to left we'd end up with ×(˜+), which uses ˜ as an operand to . But a modifier can't be used as an operand. To make it work we'd have to give 1-modifiers a higher precedence than 2-modifiers.

-

In fact, the rules for modifiers are exactly the same as those for functions, but reversed. So why is there a distinction between 1- and 2-modifiers, when for functions we can look to the left to see whether there is a left argument? The reason is that it's natural to follow a 1-modifier by a subject or function that isn't supposed to be its operand. Using an example from the last section, +˜ 3 has a subject to the right of the 1-modifier ˜. Even worse, +˜ ÷ 3 looks just like + ÷ 3, but it's two functions +˜ and ÷ applied to 3 while the version with Atop is a single function +÷ applied to 3. So the two-layer system of functions and modifiers forces modifiers to have a fixed number of operands even though every function (including those derived by applying modifiers) can be called with one or two arguments.

-

Remember that 1-modifiers are all superscripts? The characters for 2-modifiers use a different rule: each contains an unbroken circle (that is, lines might touch it but not go through it). The 2-modifiers in BQN are the combinators ∘○⊸⟜⊘, the sort-of-combinators ⌾◶⍟, and the not-at-all-combinators ⎉⚇⎊. And the functions that make that unbroken circle rule necessary are written ⌽⍉. Since every primitive is a function, 1-modifier, or 2-modifier, you can always tell what type (and role) it has: a superscript is a 1-modifier, an unbroken circle makes it a 2-modifier, and otherwise it's a function.

+

In fact, the rules for modifiers are exactly the same as those for functions, but reversed. So why is there a distinction between 1- and 2-modifiers, when for functions we can look to the left to see whether there's a left argument? The reason is that it's natural to follow a 1-modifier by a subject or function that isn't supposed to be its operand. Using an example from the last section, +˜ 3 has a subject to the right of the 1-modifier ˜. Even worse, +˜ ÷ 3 looks just like + ÷ 3, but it's two functions +˜ and ÷ applied to 3 while the version with Atop is a single function +÷ applied to 3. So the two-layer system of functions and modifiers forces modifiers to have a fixed number of operands even though every function (including those derived by applying modifiers) can be called with one or two arguments.

+

Remember how 1-modifiers are all superscripts? The characters for 2-modifiers use a different rule: each contains an unbroken circle (that is, lines might touch it but not go through it). The 2-modifiers in BQN are the combinators ∘○⊸⟜⊘, the sort-of-combinators ⌾◶⍟, and the not-at-all-combinators ⎉⚇⎊. And the functions that make that "unbroken" part necessary are written ⌽⍉. Since every primitive is a function, 1-modifier, or 2-modifier, you can always tell what type (and role) it has: a superscript is a 1-modifier, an unbroken circle makes it a 2-modifier, and otherwise it's a function.

Summary

The objects we've seen so far are:

diff --git a/docs/tutorial/list.html b/docs/tutorial/list.html index 6b423da0..2d0e9be9 100644 --- a/docs/tutorial/list.html +++ b/docs/tutorial/list.html @@ -73,7 +73,7 @@
-

Finally, strand notation is a shortcut for simple lists, like one that just lists a few numbers. It's written with the ligature , which has a higher precedence than either functions or operators (and on the BQN keyboard, the ligature is written with a backslash, then a space). A sequence of values joined with ligatures becomes a list, so that for example the following two expressions are equivalent:

+

Finally, strand notation is a shortcut for simple lists, like one that just lists a few numbers. It's written with the ligature , which has a higher precedence than either functions or operators (and is typed with backslash-space on the BQN keyboard). A sequence of values joined with ligatures becomes a list, so that for example the following two expressions are equivalent:

2,+,-
 2+-
 
@@ -113,7 +113,7 @@ -

Lists are just one-dimensional arrays. Types are divided into data types, which tend to have a subject role, and operation types, which tend to have a role matching their type. Also, any value that's not an array, such as everything we used in the last tutorial, is called an atom.

+

Lists are just one-dimensional arrays. These types are divided into data types, which correspond to a subject role, and operation types, which each have a matching role. Also, any value that's not an array, such as everything we used in the last tutorial, is called an atom.

Arithmetic on lists

Arithmetic functions automatically apply to each element of a list argument. If both arguments are lists, they have to have the same length, and they're matched up one element at a time.

↗️
    ÷ 2,3,4
@@ -181,8 +181,9 @@
 "reward"
 

With a left argument means Rotate instead, and shifts values over by the specified amount, wrapping those that go off the end to the other side. A positive value rotates to the left, and a negative one rotates right.

-↗️
    2  0,1,2,3,4
+↗️
    2  0,1,2,3,4
 ⟨ 2 3 4 0 1 ⟩
+
     ¯1  "bcdea"
 "abcde"
 
@@ -206,7 +207,7 @@ -

The 1-modifier Each (¨) applies its operand to every element of a list argument: it's the same as map in a functional programming language. With two list arguments (which have to have the same length), Each pairs the corresponding elements from each, a bit like a zip function. If one argument is a list and one's an atom, the atom is reused every time instead.

+

The 1-modifier Each (¨) applies its operand to every element of a list argument, like map in a functional programming language. With two list arguments, Each pairs the corresponding elements like arithmetic does, or a bit like a zip function (unlike arithmetic, it only goes one level deep). If one argument is a list and one's an atom, the atom is reused every time instead.

↗️
    ¨ "abcd""ABCDEF""01"
 ⟨ "dcba" "FEDCBA" "10" ⟩
 
@@ -229,7 +230,7 @@
 3
 

With this evaluation order, -´ gives the alternating sum of its argument. Think of it this way: the left argument of each - is a single number, while the right argument is made up of all the numbers to the right subtracted together. So each - flips the sign of every number to its right, and every number is negated by all the -s to its left. The first number (1 above) never gets negated, the second is negated once, the third is negated twice, returning it to its original value… the signs alternate.

-

Hey, isn't it dissonant that the first, second, and third numbers are negated zero, one, and two times? If they were the zeroth, first, and second it wouldn't be…

+

Hey, isn't it dissonant that the first, second, and third numbers are negated zero, one, and two times? Not if you call them the zeroth, first, and second…

You can fold with the Join To function to join several lists together:

↗️
    ´  "con", "cat", "enat", "e" 
 "concatenate"
@@ -239,7 +240,7 @@
 "concatenate"
 

Example: base decoding

-

Some people like to imagine that robots or other techno-beings speak entirely in binary-encoded ASCII, like for instance "01001110 01100101 01110010 01100100 00100001". This is dumb for a lot of reasons, and the encoded text probably just says something inane, but you're a slave to curiosity and can't ignore it. Are one and a half tutorials of BQN enough to clear your conscience?

+

Some people like to imagine that robots or other techno-beings speak entirely in binary-encoded ASCII, like for instance "01001110 01100101 01110010 01100100 00100001". This is dumb for a lot of reasons, and the encoded text probably just says something inane, but you're a slave to curiosity and can't ignore it. Are one and two-thirds tutorials of BQN enough to clear your conscience?

@@ -259,7 +260,7 @@ ↗️
     8
 ⟨ 0 1 2 3 4 5 6 7 ⟩
 
-

Natural numbers in BQN start at 0. I'll get to the second function in a moment, but first let's consider how we'd decode just one number in binary. I'll pick a smaller one: 9 is 1001 in binary. Like the first 1 in decimal 1001 counts for one thousand or 103, the first one in binary 1001 counts for 8, which is 23. We can put each number next to its place value like this:

+

Natural numbers in BQN start at 0. I'll get to the second primitive in a moment, but first let's consider how we'd decode just one number in binary. I'll pick a smaller one: 9 is 1001 in binary. Like the first 1 in decimal 1001 counts for one thousand or 103, the first one in binary 1001 counts for 8, which is 23. We can put each number next to its place value like this:

↗️
    8421 ¨ 1001
 ⟨ ⟨ 8 1 ⟩ ⟨ 4 0 ⟩ ⟨ 2 0 ⟩ ⟨ 1 1 ⟩ ⟩
 
@@ -363,7 +364,7 @@ 9

Summary

-

There are three types of syntax that create lists: the "string literal" for lists of characters and either enclosing angle brackets ⟨⟩ with , or or newline characters or connecting ligatures for lists with arbitrary elements. The ligature has a higher precedence than functions or modifiers, so we should add it to our precedence table:

+

There are three types of syntax that create lists. "string literal" gives a list of characters. For arbitrary lists, use enclosing angle brackets ⟨⟩ with , or or newline between elements, or just connect elements with ligatures . The ligature has a higher precedence than functions or modifiers and might require parentheses, so we should add it to our precedence table:

@@ -395,8 +396,8 @@
-

The elements of a list can have any syntactic role; it's ignored and the list as a whole is a subject.

-

We introduced a few new primitives. The links below go to the full documentation pages for them.

+

The elements of a list can have any syntactic role. It's ignored and the list as a whole is a subject.

+

We introduced a few new primitives. The links in the table go to their full documentation pages.

@@ -438,5 +439,5 @@
-

Additionally, we saw that the arithmetic functions work naturally on lists, automatically applying to every element of a single list argument or pairing up the elements of two list arguments.

+

And we saw that the arithmetic functions work naturally on lists, automatically applying to every element of a single list argument or pairing up the elements of two list arguments.

Even this small amount of list functionality is enough to tackle some little problems. We haven't even introduced a function notation yet!

-- cgit v1.2.3