From 2afb23928e1984d475cc460e1672e8f6fa0e4dbe Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Wed, 11 Aug 2021 17:21:31 -0400 Subject: Allow clicking on header to get fragment link --- docs/tutorial/combinator.html | 24 ++++++++++++------------ docs/tutorial/expression.html | 16 ++++++++-------- docs/tutorial/index.html | 2 +- docs/tutorial/list.html | 16 ++++++++-------- docs/tutorial/variable.html | 14 +++++++------- 5 files changed, 36 insertions(+), 36 deletions(-) (limited to 'docs/tutorial') diff --git a/docs/tutorial/combinator.html b/docs/tutorial/combinator.html index 5052aad1..a9cd7627 100644 --- a/docs/tutorial/combinator.html +++ b/docs/tutorial/combinator.html @@ -4,11 +4,11 @@ BQN Tutorial: Combinators -

Tutorial: Combinators

+

Tutorial: Combinators

BQN has a normal enough curly-brace syntax for functions and so on. I don't want to talk about it just yet. Before you get to thinking about how to write FORTRAN in any language in BQN, let's see if we can acquire some instincts about idiomatic BQN the way that only being stuck in a tightly restricted and horribly confining programming environment can accomplish.

There are benefits to being tightly restricted and horribly confined! In programming. I don't just mean that it forces you to learn new techniques like I said, I mean that using the restricted style we will learn is actually a better way to write portions of a program. This is because a restriction you apply in one part of a program is a promise you can rely on in the rest of the program. So what are we giving up, and what can we rely on in return?

Tacit programming does not use variables during the execution of a function (but you might use them for convenience in order to construct a tacit program). Variables allow you to use any accessible value in the program with the same level of ease. Tacit code doesn't. In fact it becomes pretty unusable when more than about three values are active at once. One consequence is that tacit code won't cause confusion by modifying far-away variables. But something unique to the tacit paradigm is that when only a small number of values are active—which is always true in a small enough portion of a program!—it has more powerful ways to describe the way these values flow through the program. The main way it achieves this is with combinators.

-

What's a combinator?

+

What's a combinator?

(It's when you can't stop adding suffixes to "combine"). In the first tutorial, we briefly presented three combinators, , ˜, and ˙. These are functions or modifiers that produce a result from their inputs (arguments or operands) only by applying functions to arguments. For example, let's look at a composition:

↗️
    |- 6
 6
@@ -156,7 +156,7 @@
   
 
 
-

Comparisons

+

Comparisons

@@ -205,7 +205,7 @@ <@ 1 -

Booleans

+

Booleans

The return values 0 and 1 are natural choices because BQN has no dedicated boolean type: instead, in BQN, the word boolean indicates a number that's 0 or 1, much like "natural number" selects a subset of the possible numbers. This is a choice that might be at odds with your own programming experience, and especially clashes with the world of typed functional programming, where even using the boolean type rather than a dedicated type for an option might be considered a code smell! The design principle guiding this decision, and most of BQN's type system, is that there should only be one type that accomplishes any given programming task. Any distinctions that it has are there because they are really necessary: conflating numbers and characters would make working with strings too difficult, and functions can't really be combined with modifiers because one-argument functions and 1-modifiers take their inputs on different sides.

The advantage of this strategy is that you will spend much less time thinking about types when writing programs. The decisions are already made: if there are a few things, they go in a list; if there a few possible values in a qualitative category then they should be labelled with numbers. And if some value has multiple interpretations then BQN is less likely to require an explicit conversion between these. For example, while the result of = might be taking to say whether two atoms have equal values, maybe it also says how many times the atom on the left appears in the right argument—which is at most one, because there's only one right argument. A silly distinction, or is it? An important property of counts is that we can add them together, for instance, to find how many times the letter "e" appears in a string.

↗️
    'e' = "George Boole"
@@ -218,7 +218,7 @@
 3
 

This, a well-typed and well-spoken programmer should declare, is an outrage! The purpose of types is to protect us from applying functions to types they're not intended for, because the most likely result is that such an application doesn't make sense. Static types provide a valuable service by catching these dangerous actions at compile time and allowing a programmer to prove that they never happen. Well… this is all true. BQN chooses not to pay the type price of this service and so doesn't get the type protection. And it helps that it's consistent about this choice, so you know that BQN's types are never the answer to these sorts of safety concerns. You will have to find a different way to avoid type errors: perhaps just programming carefully in a small or one-off program, and testing and code review or even external tools in a large one. All that said, I think programmers from outside the array programming world (and even many inside!) drastically underestimate how often a boolean value is really just a narrow-minded take on a counting number.

-

Comparing whole arrays

+

Comparing whole arrays

<
@@ -250,7 +250,7 @@ 23423322 1 -

Length, rank, and depth

+

Length, rank, and depth

I said above that the comparison functions might have a different meaning when called with only one argument, so let's cover a few of these.

Length () gives the number of elements in a list. For atoms it returns 1; it can't result in an error.

↗️
     "testing"
@@ -281,9 +281,9 @@
 3
 

You probably won't end up using Depth too much. The data in a typical program has a fixed, known depth, so there's no point in asking BQN what it is. But it might be useful if you want to write a utility function that's flexible about its input. 0<≡a is the idiomatic way to test whether a is an array.

-

Composition

+

Composition

We've discussed Atop (), but hopefully you've intuited that it's not the end of the story as far as compositions go. In fact BQN has three more modifiers that could reasonably be interpreted as varieties of composition.

-

Over

+

Over

@@ -375,7 +375,7 @@ -

Before and After

+

Before and After

Atop () and Over () are both symmetric in some sense: with two arguments, (FG)˜ is F(G˜), and (FG)˜ is (F˜)G. Put another way, reversing the order of arguments to Atop or Over as a whole is the same as reversing the order of every two-argument function inside—G for FG and F for FG. If it's not obvious why this is the case, work it out for yourself by walking through how these functions would apply to their arguments! This causes their diagrams to be symmetric as well. Swap (˜) also has a symmetric diagram, and it's very easy to show that it's symmetric: take a look at (F˜)˜ and (F˜)˜. In both cases I started with F˜, but in one case I applied ˜ to the entire function and in the other I applied it on the inside, to F only. And I won't tell you which is which.

@@ -497,7 +497,7 @@ ⟨ 0 0.109375 0.1875 0.234375 0.25 0.234375 0.1875 0.109375 ⟩

Our list of arguments stops before reaching 1, because 8 doesn't include 8. If we wanted a list from 0 to 1 inclusive, we'd need to divide by 7 (that is, 8-1) instead of 8. We can do this as well! But first we need to understand some other ways to apply Before and After.

-

Bind

+

Bind

We showed in the first tutorial that a modifier's operand doesn't have to be a function, but can also be a data value. That hasn't come up yet, except for a cryptic use of "Bind" (?) in the function (2⋆↕8)ר from the last tutorial. How does that work? Some kind of secret identity for Before and After?

↗️
    1+ 5
 6
@@ -550,7 +550,7 @@
 ↗️
    (↕÷-1) 8
 ⟨ 0 0.14285714285714285 0.2857142857142857 0.42857142857142855 0.5714285714285714 0.7142857142857143 0.8571428571428571 1 ⟩
 
-

Base decoding continued

+

Base decoding continued

We're speeding up a bit now, so in the examples below it might take some time for you to break down what I did and why. Remember that you can open any expression in the REPL in order to change parts of it or view the syntax. And don't get discouraged just because of how long it takes to understand a line of code! First, you'll surely get faster in fitting the pieces together. Second, a line of BQN often has more code in it than a line in other languages, because primitives have such short names. Think about how much functionality you can read and understand rather than how few lines you get through.

In the last tutorial I went over a way to decode a list of strings containing binary codes for ASCII characters:

↗️
    @ + +´¨ (2⋆↕8)ר '0' -˜ "01000010""01010001""01001110"
@@ -597,7 +597,7 @@
 ↗️
    (@+ ·+(2×)´¨ -'0') "01000010""01010001""01001110"
 "BQN"
 
-

Summary

+

Summary

BQN has a full complement of comparison functions, which are pervasive (work on atoms only) like arithmetic functions. The non-pervasive functions Match () and Not Match () compare entire arrays. Comparison functions return 1 if the comparison holds and 0 if it doesn't; these two numbers make up the "booleans".

diff --git a/docs/tutorial/expression.html b/docs/tutorial/expression.html index 840ec4fd..89a43380 100644 --- a/docs/tutorial/expression.html +++ b/docs/tutorial/expression.html @@ -4,8 +4,8 @@ Tutorial: BQN expressions -

Tutorial: BQN expressions

-

Arithmetic

+

Tutorial: BQN expressions

+

Arithmetic

All right, let's get started! Since you can run BQN online from the REPL there aren't any real technical preliminaries, but if you'd like to look at non-web-based options head over to running.md.

In the code blocks shown here, input is highlighted and indented, while output is not colored or indented. To experiment with the code, you can click the ↗️ arrow in the top right corner to open it in the REPL.

↗️
    2 + 3
@@ -102,7 +102,7 @@
     # \ shift
 

In case you're wondering, Logarithm—the other inverse of Power—is written . We'll see how that works when we introduce in the section on 1-modifiers.

-

Compound expressions

+

Compound expressions

It's sometimes useful to write programs with more than one function in them. Here is where BQN and any sort of normality part ways.

↗️
    2×3 - 5
 ¯4
@@ -157,7 +157,7 @@
 
 

The online REPL includes a tool to create diagrams like the one shown above. To enable it, click the "explain" button. Then a diagram of your source code will be shown above the result each time you run an expression.

The following rule might help you to internalize this system in addition to identifying when parentheses are needed: an expression never needs to end with a parenthesis, or have two closing parentheses in a row. If it does, at least one set of parentheses can be removed without changing the meaning.

-

One or two arguments?

+

One or two arguments?

What about functions without a left argument? Let's find an equation with lots of square roots in it… looks good.

↗️
     3 + 2 × 2
 2.414213562373095
@@ -227,7 +227,7 @@
 
  • A set of parentheses has the same role as whatever's inside it.
  • 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!

    -

    Character arithmetic

    +

    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'
    @@ -279,7 +279,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

    +

    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 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'
    @@ -332,7 +332,7 @@
     

    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 screen. 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

    +

    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 works a lot like mathematical composition, except that it's extended to use one or two arguments. These arguments are passed to the function on the right, and the result 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
    @@ -384,7 +384,7 @@
     

    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.

    -

    Summary

    +

    Summary

    The objects we've seen so far are:

    diff --git a/docs/tutorial/index.html b/docs/tutorial/index.html index 396aabe4..85a2431f 100644 --- a/docs/tutorial/index.html +++ b/docs/tutorial/index.html @@ -4,7 +4,7 @@ BQN tutorials -

    BQN tutorials

    +

    BQN tutorials

    BQN tutorials explain how to approach and use the language as a newcomer (or they try; please get in touch if you find that they skip ahead!). Tutorials are meant to explain key ideas and may ignore details that would be included in the documentation; also unlike the documentation they're meant to be read in order, as each tutorial will build on ideas from the previous ones.

    Tutorials assume (pretty presumptively, really. Disgusting.) that you are already motivated to learn BQN and use simple rather than flashy examples. Documents to induce motivation beyond the README are not yet available. Do feel free to skim or jump around if you find you are reading a lot of things that are already obvious.

    The tutorials available so far:

    diff --git a/docs/tutorial/list.html b/docs/tutorial/list.html index a44c30b4..0e032b98 100644 --- a/docs/tutorial/list.html +++ b/docs/tutorial/list.html @@ -4,7 +4,7 @@ BQN Tutorial: Working with lists -

    Tutorial: Working with lists

    +

    Tutorial: Working with lists

    Enough with all these preliminaries like learning how to read basic expressions. Let's get into what makes BQN special.

    ↗️
        1, 2, 3
     ⟨ 1 2 3 ⟩
    @@ -14,7 +14,7 @@
     ⟨ 2 3 4 ⟩
     

    There we go. Now in BQN arrays are not just lists, which are a 1-dimensional data structure, but can have any number of dimensions. In this tutorial we're going to discuss lists only, leaving the 5-dimensional stuff for later. So we're really only seeing the power of K, an APL-family language that only uses lists (and dictionaries, which BQN doesn't have). K was powerful enough for Arthur Whitney to found two companies and make millions and millions of dollars, and BQN's compiler also runs almost entirely on lists, so this is probably enough power for one webpage.

    -

    List notation

    +

    List notation

    There are three kinds of list notation in BQN. Each of them has a subject role overall, even if expressions used inside it might have other roles. First, a string is a list of characters, and is written by placing those characters in double quotes.

    "Text!"
     
    @@ -86,7 +86,7 @@ 0(12) ⟨ 0 ⟨ 1 2 ⟩ ⟩ -

    BQN types

    +

    BQN types

    Now that all six BQN types have been introduced, let's make a table:

    @@ -111,7 +111,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.

    -

    Arithmetic on lists

    +

    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
     ⟨ 0.5 0.3333333333333333 0.25 ⟩
    @@ -132,7 +132,7 @@
          10, 2030  +  12, 3 
     ⟨ ⟨ 11 12 ⟩ ⟨ 23 33 ⟩ ⟩
     
    -

    Some list functions

    +

    Some list functions

    @@ -183,7 +183,7 @@ ¯1"bcdea" "abcde" -

    …and modifiers

    +

    …and modifiers

    @@ -235,7 +235,7 @@ ↗️
          "con", "cat", "enat", "e" 
     "concatenate"
     
    -

    Example: base decoding

    +

    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?

    ¨
    @@ -359,7 +359,7 @@ ERROR +(+˜)´"1001"-'0' 9 -

    Summary

    +

    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:

    diff --git a/docs/tutorial/variable.html b/docs/tutorial/variable.html index 33f1e48c..e8a1f06e 100644 --- a/docs/tutorial/variable.html +++ b/docs/tutorial/variable.html @@ -4,7 +4,7 @@ BQN Tutorial: Variables -

    Tutorial: Variables

    +

    Tutorial: Variables

    To take a proud denizen of the eternal cosmos of values, held for a fleeting instant by the course of code, and bind it. Tie it down with a name, failing always to alter its inner nature but allowing context to reform its outer appearance. So labelled, perhaps through the progress of time it will know escape, or else find itself passed through one bond to another, ever tethered. It's a task to be approached only with respect.

    ↗️
        hey  "Hi there"
     
    @@ -12,7 +12,7 @@
     "Hi there, World!"
     

    Like that.

    -

    Defining variables

    +

    Defining variables

    BQN uses the left-pointing arrow to define variables, as shown above. Most of the time it's best to use it in a plain way, with just the name and its definition, but it's also possible to define multiple variables using list notation, or to define a variable as part of a larger expression that continues to the left (in terms of precedence, behaves like a function, but it isn't one—it's a part of syntax).

    ↗️
        pieten   π, 1, 10 
     ⟨ 3.141592653589793 2.718281828459045 10 ⟩
    @@ -40,7 +40,7 @@ ERROR
     ERROR
     

    It's an odd distinction to have when your program is just one long sequence of statements, because there's only ever one arrow you can use: it just changes annoyingly after you define the variable for the first time. With multiple scopes this isn't the case: if you start a new scope inside another, then you'll still be able to use variables from the outside scope. Then lets you change the value of one of these variables while allows you to define your own. If you're coming from a typical curly-brace language, you'd say that both declares and assigns a variable, while only assigns it.

    -

    Variable roles

    +

    Variable roles

    ↗️
        BQN  "[many pages of specification]"
     ERROR
     
    @@ -104,7 +104,7 @@ ERROR ↗️
        1_000_000
     1000000
     
    -

    Function assignment

    +

    Function assignment

    While you could build up a script by computing values and assigning them names, the main way to use assignment in tacit programming is to give names to functions, not data. For example, we might name the base-2 conversion function from our last tutorial:

    ↗️
        Base2  +(2×)´
     
    @@ -130,7 +130,7 @@ ERROR
         Base2 6
     16
     
    -

    Modifying part of an array

    +

    Modifying part of an array

    You cannot modify part of an array. You can't modify an array: an array that differs a little bit from another array is a different array. And this isn't just a terminology choice: it has real effects on how BQN arrays behave and even which arrays are representable, as we'll discuss later.

    But say I have a list, and I want to subtract one from one of the elements. With the understanding that the resulting list is different from the first one, BQN allows this!

    ↗️
        "BQN"            # A list of characters
    @@ -252,7 +252,7 @@ ERROR
     "abcdEFgh"
     

    (Here I've snuck in a train 2 4 to combine the two functions. As an exercise, you might try to write that function using combinators instead, and as an extra hard exercise you might then ponder why someone would want to add trains to a language).

    -

    Identity functions

    +

    Identity functions

    @@ -282,7 +282,7 @@ ERROR "left"

    They are not complicated functions: if you're confused it's because you don't understand why anyone would ever use them. Indeed, it's harder to see why these functions are useful than to see what they do. That is a fact.

    -

    Modified assignment

    +

    Modified assignment

    Let's revisit our question about modifying an array. As we said, the answer to "how do I modify part of an array?" is simply that you can't, and that the question doesn't make sense. But there's a seemingly similar question with a very different answer: "how do I modify part of a variable whose value is an array?" This is because unlike an array, a variable isn't defined by the value it has, but by the name used to refer to it (and the scope it resides in). Here's how we would modify the variable a:

    ↗️
        a  4            # First it's a number
         a
    -- 
    cgit v1.2.3