From c5eef0418df2ae6a97c54839fa010ff60d96f78b Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Sat, 8 Jan 2022 16:14:51 -0500 Subject: =?UTF-8?q?Add=20error=20messages=20to=20generated=20markdown=20do?= =?UTF-8?q?cs=20with=20=E2=80=A2CurrentError=20(fixes=20#22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/doc/arrayrepr.html | 2 +- docs/doc/assert.html | 8 ++++---- docs/doc/block.html | 4 ++-- docs/doc/enclose.html | 2 +- docs/doc/find.html | 2 +- docs/doc/join.html | 4 ++-- docs/doc/lexical.html | 6 +++--- docs/doc/map.html | 6 +++--- docs/doc/match.html | 2 +- docs/doc/order.html | 4 ++-- docs/doc/pair.html | 2 +- docs/doc/pick.html | 8 ++++---- docs/doc/rebqn.html | 2 +- docs/doc/reshape.html | 4 ++-- docs/doc/reverse.html | 6 +++--- docs/doc/select.html | 6 +++--- docs/doc/undo.html | 2 +- docs/style.css | 2 ++ docs/tutorial/list.html | 2 +- docs/tutorial/variable.html | 8 ++++---- 20 files changed, 42 insertions(+), 40 deletions(-) (limited to 'docs') diff --git a/docs/doc/arrayrepr.html b/docs/doc/arrayrepr.html index a6da93aa..43c0ad51 100644 --- a/docs/doc/arrayrepr.html +++ b/docs/doc/arrayrepr.html @@ -167,7 +167,7 @@ "-'×%""*" "-'×%"*" # Escaping failure -ERROR +Error: Unclosed quote

Even special characters like a newline can appear in a string literal, so that string literals are automatically multi-line.

Brackets

diff --git a/docs/doc/assert.html b/docs/doc/assert.html index 2e112283..1d56a9ed 100644 --- a/docs/doc/assert.html +++ b/docs/doc/assert.html @@ -9,19 +9,19 @@ ↗️
    ! 2=2  # Passed
 1
     ! 2=3  # Failed
-ERROR
+Error: Assertion error
 

To pass, the right argument must be exactly the number 1; any other value causes an error. For example, an array of 1s still causes an error; use ´ to convert a boolean array to a single boolean that indicates whether all of its values are true.

↗️
    ! (∧=∨¬)⌜˜ 2
-ERROR
+Error: 2‿2⥊1‿1‿1‿1
     ! ´ (∧=∨¬)⌜˜ 2
 1
 

Assert can take a left argument, which gives a message to be associated with the error. It's typical to use a string for the left argument in order to display it to the programmer, but the left argument can be any value.

↗️
    "Message" ! 0
-ERROR
+Error: Message
     ,"abc",˜ ! '0'  # Okay this is not a very helpful printout
-ERROR
+Error: ⟨∘,"abc",˜⟩
 

Computing the error message on demand

Because the left argument to a function is always computed before the function is called, Assert doesn't let you compute the error message only if there's an error. This might be a problem if the error message computation is slow or has side effects. There are a few ways to work around the issue:

diff --git a/docs/doc/block.html b/docs/doc/block.html index 5e957e1e..64643e7d 100644 --- a/docs/doc/block.html +++ b/docs/doc/block.html @@ -201,7 +201,7 @@

If no header is compatible, the call results in an error.

↗️
    3 CaseAdd 3
-ERROR
+Error: No header matched arguments
 

Case headers

A special rule allows for convenient case-matching syntax for one-argument functions. In any function header with one argument, the function name can be omitted as long as the argument is not a plain identifier—it must be 𝕩 or a compound value like a list to distinguish it from an immediate block label.

@@ -239,6 +239,6 @@ ERROR

This structure is still constrained by the rules of block bodies: each instance of ; is a separate scope, so that variables defined before a ? don't survive past the ;.

↗️
    { 0=n𝕩 ?  ; n } "abc"
-ERROR
+Error: Undefined identifier
 

This is the main drawback of predicates relative to guards in APL dfns (also written with ?), while the advantage is that it allows multiple expressions, or extra conditions, after a ?. It's not how I would have designed it if I just wanted to make a syntax for if statements, but it's a natural fit for the header system.

diff --git a/docs/doc/enclose.html b/docs/doc/enclose.html index aac48d68..2a401603 100644 --- a/docs/doc/enclose.html +++ b/docs/doc/enclose.html @@ -39,7 +39,7 @@

In this case each call to +˝ returns a cell of the result. The result is a list, so its cells are units! Here, Cells (˘) "hides" one axis from its operand, and the operand +˝ reduces out an axis, leaving zero axes—until Cells assembles the results, putting its axis back. In this case, +´ would also be tolerated. But it's wrong, because each result really should be a zero-axis array. We can reveal this by making an array whose elements aren't atoms.

↗️
    +´˘ 2,"ab"3,"ABC"
-ERROR
+Error: >: Elements didn't have equal shapes (contained ⟨2⟩ and ⟨3⟩)
     +˝˘ 2,"ab"3,"ABC"
 ⟨ "ac" "ACE" ⟩
 
diff --git a/docs/doc/find.html b/docs/doc/find.html index 85f26297..09632469 100644 --- a/docs/doc/find.html +++ b/docs/doc/find.html @@ -37,7 +37,7 @@ ⟨⟩ 9 "short" -ERROR +Error: 𝕨↕𝕩: Window length 𝕨 must be at most axis length plus one 0 ´ "loooooong" "short" 0 diff --git a/docs/doc/join.html b/docs/doc/join.html index 56975907..9f93eae4 100644 --- a/docs/doc/join.html +++ b/docs/doc/join.html @@ -34,7 +34,7 @@

For this definition to work, major cells of 𝕨 and 𝕩 have to have the same shape. That means that 𝕨(1↓≢)𝕩, and the shape of the result is the sum of the lengths of 𝕨 and 𝕩 followed by their shared major cell shape: to use a self-referential definition, the final shape is given by + (1↓≢) for arguments of equal rank.

↗️
    a  25b  # Shapes don't fit
-ERROR
+Error: ∾: Lengths not matchable (3‿4 ≡ ≢𝕨, 2‿5 ≡ ≢𝕩)
 

Join To will also allow arguments with ranks that are one apart. In this case, the smaller-rank argument is treated as a major cell in its entirety. If for example 𝕨<=𝕩, then we must have (𝕨)1↓≢𝕩, and the result shape is 1+⊑≢𝕩.

↗️
    4230  a
@@ -65,7 +65,7 @@
 "abcdefg"
 
     "abcd"  # Result has to be rank 0, impossible
-ERROR
+Error: ∾𝕩: 𝕩 must have an element with rank at least =𝕩
 

However, Join has higher-dimensional uses as well. Given a rank-m array of rank-n arrays (requiring mn), it will merge arrays along their first m axes. For example, if the argument is a matrix of matrices representing a block matrix, Join will give the corresponding unblocked matrix as its result.

↗️
     m  (31425) ¨ 23⥊↕6
diff --git a/docs/doc/lexical.html b/docs/doc/lexical.html
index ec3f6567..dcc5fef0 100644
--- a/docs/doc/lexical.html
+++ b/docs/doc/lexical.html
@@ -62,7 +62,7 @@
 

Each scope can only define a given name once. Trying to shadow a name that's in the current scope and not a higher one gives an error at compilation.

↗️
    { inc3  inc4 }
-ERROR
+Error: Redefinition
 

Let's go all in on shadowing and make a modifier that creates its own copies of counter and inc, returning a custom version of the Count function above.

↗️
    _makeCount  { counterinc𝕗  { counter + 𝕩 × inc } }
@@ -85,7 +85,7 @@ ERROR
 

But you'll still get an error if the variable is used before its definition is run. Unlike the single-level case, this is a runtime error and only appears when the variable is actually accessed.

↗️
    { 2+d }  d¯2
-ERROR
+Error: Reading variable before its defined
 

Why define things this way? It's easier to see if you imagine the variable used is also a function. It's normal for a function to call other functions defined at the top level, of course. And it would be pretty unpleasant for BQN to enforce a specific ordering for them. It would also make recursive functions impossible except by using 𝕊, and mutually recursive ones completely impossible. A simple rule that makes all these things just work smoothly seems much better than any alternative.

Closures

@@ -140,6 +140,6 @@ ERROR

Only code with access to a variable can modify it! This means that if none of the code in a variable's scope modifies it, then the variable is a constant in each environment that contains it (not necessarily across environments). That is, constant once it's defined: it's still possible to get an error if the variable is accessed before being defined.

↗️
    { { a }  a4 }
-ERROR
+Error: Reading variable before its defined
 

With lexical scoping, variable mutation automatically leads to mutable data. This is because a function or modifier that depends on the variable value changes its behavior when the variable changes. For further discussion see the documentation on mutable objects.

diff --git a/docs/doc/map.html b/docs/doc/map.html index b7c4afe5..f84421cf 100644 --- a/docs/doc/map.html +++ b/docs/doc/map.html @@ -219,7 +219,7 @@

If the argument lengths don't match then Each gives an error. This contrasts with zip in many languages, which drops elements from the longer argument (this is natural for linked lists). This flexibility is rarely wanted in BQN, and having an error right away saves debugging time.

↗️
    "ABC" ¨ "01234"
-ERROR
+Error: Mapping: Expected equal shape prefix (⟨3⟩ ≡ ≢𝕨, ⟨5⟩ ≡ ≢𝕩)
 

Arguments can have any shape as long as the axis lengths match up. As with Table, the result elements don't depend on these shapes but the result shape does.

↗️
    (>203010,504060) +¨ 210321
@@ -230,10 +230,10 @@ ERROR
 

But arguments don't have to have exactly the same shape: just the same length along corresponding axes. These axes are matched up according to the leading axis convention, so that one argument's shape has to be a prefix of the other's. With equal ranks, the shapes do have to match as we've seen above.

↗️
     (026@) ¨ 010  # Too small
-ERROR
+Error: Mapping: Expected equal shape prefix (0‿2‿6 ≡ ≢𝕨, 0‿1 ≡ ≢𝕩)
      (026@) ¨ 020  # Just right
 ⟨ 0 2 6 ⟩
      (026@) ¨ 030  # Too large
-ERROR
+Error: Mapping: Expected equal shape prefix (0‿2‿6 ≡ ≢𝕨, 0‿3 ≡ ≢𝕩)
 

Leading axis agreement is described further here.

diff --git a/docs/doc/match.html b/docs/doc/match.html index 1e752f6f..95336280 100644 --- a/docs/doc/match.html +++ b/docs/doc/match.html @@ -18,7 +18,7 @@ 0 "abc" = "ab" # Mismatched shapes -ERROR +Error: =: Expected equal shape prefix (⟨3⟩ ≡ ≢𝕨, ⟨2⟩ ≡ ≢𝕩) "abc" "ab" 0 diff --git a/docs/doc/order.html b/docs/doc/order.html index bd13f64a..1916167d 100644 --- a/docs/doc/order.html +++ b/docs/doc/order.html @@ -88,9 +88,9 @@

The two Bins functions are written with the same symbols and as Grade, but take two arguments instead of one. More complicated? A little, but once you understand Bins you'll find that it's a basic concept that shows up in the real world all the time.

Bins behaves like a search function with respect to rank: it looks up cells from 𝕩 relative to major cells of 𝕨. However, there's an extra requirement: the left argument to Bins is already sorted according to whichever ordering is used. If it isn't, you'll get an error.

↗️
    56241  3
-ERROR
+Error: ⍋: 𝕨 must be sorted
     03479  3
-ERROR
+Error: ⍒: 𝕨 must be sorted in descending order
 

Given this, the simplest definition of 𝕨𝕩 (or 𝕨𝕩) is that for each cell in 𝕩 of rank (=𝕨)-1, it counts the number of major cells from 𝕨 that come earlier in the ordering, or match that cell.

Why would that be useful? How about an example. A pinball machine has some high scores on it. You play, and your rank is the number of scores higher than yours (in this case, if you tie someone's score, you won't unseat them).

diff --git a/docs/doc/pair.html b/docs/doc/pair.html index 46f4c82d..a4bf9b75 100644 --- a/docs/doc/pair.html +++ b/docs/doc/pair.html @@ -51,7 +51,7 @@

And the arguments to Couple must have the same shape, while Enlist takes any two arguments.

↗️
    "abc"  "defg"
-ERROR
+Error: ≍: 𝕨 and 𝕩 must have equal shapes (⟨3⟩ ≡ ≢𝕨, ⟨4⟩ ≡ ≢𝕩)
 
     "abc"  "defg"
 ⟨ "abc" "defg" ⟩
diff --git a/docs/doc/pick.html b/docs/doc/pick.html
index d3166012..a5e9392b 100644
--- a/docs/doc/pick.html
+++ b/docs/doc/pick.html
@@ -57,9 +57,9 @@
 

If 𝕩 is empty then First results in an error, like Pick.

↗️
     ""
-ERROR
+Error: ⊑: Argument cannot be empty
      π
-ERROR
+Error: ⊑: Argument cannot be empty
 

In APL it's common to get the last element of a list with an idiom that translates to ⊑⌽, or First-Reverse. In BQN the most straightforward way is to select with index ¯1 instead. I also sometimes use Fold with the Right identity function.

↗️
    ⊑⌽ "last"
@@ -84,7 +84,7 @@ ERROR
 

These indices have to be lists, since if they're numbers it just looks like 𝕨 is one list index.

↗️
    2,1,0,¯1  "abc"  # 𝕩 doesn't have rank 4!
-ERROR
+Error: ⊑: Picking item at wrong rank (index 2‿1‿0‿¯1 in array of shape ⟨3⟩)
 
     2,1,0,¯1 ¨ "abc"
 "cbac"
@@ -124,5 +124,5 @@ ERROR
               ┘
 
     ⟨⟨2,3,1  a  # 1 isn't a valid index
-ERROR
+Error: ⊑: 𝕨 contained list with mixed-type elements
 
diff --git a/docs/doc/rebqn.html b/docs/doc/rebqn.html index 9911d7c0..81a00418 100644 --- a/docs/doc/rebqn.html +++ b/docs/doc/rebqn.html @@ -46,7 +46,7 @@ ↗️
    doNot  •ReBQN {repl"loose"}
 
     DoNot "b" # surprised when this fails
-ERROR
+Error: Undefined identifier
 

The difference in "strict" and "loose" is that a loose REPL can define a variable again, which just changes its value (under the covers, the is treated as a ).

↗️
    Do "a ← ¯1"
diff --git a/docs/doc/reshape.html b/docs/doc/reshape.html
index e9523103..2dbceced 100644
--- a/docs/doc/reshape.html
+++ b/docs/doc/reshape.html
@@ -147,7 +147,7 @@
 ⟨ 135 136 137 145 146 147 235 236 237 245 246 247 135 136 137 ⟩
 
     4  0
-ERROR
+Error: ⥊: Empty 𝕩 and non-empty result
 

Reshape is the idiomatic way to make an array filled with a constant value (that is, where all elements are the same) when you know what shape it should have. For an atom element, reshape it directly; for an arbitrary element, first enclose it to create a unit, and then reshape it.

↗️
    34  0
@@ -181,7 +181,7 @@ ERROR
 

These values are just BQN primitives of course. They're not called by Reshape or anything like that; the primitives are just chosen to suggest the corresponding functionality.

Here's an example of the four cases. If we try to turn five elements into two rows, gives an error, drops the last element, uses the first element again, and uses a fill element (like 6"abcde" would).

↗️
    2  "abcde"
-ERROR
+Error: ⥊: Shape must be exact when reshaping with ∘
 
     2  "abcde"
 ┌─    
diff --git a/docs/doc/reverse.html b/docs/doc/reverse.html
index 49d52f50..c90f22f2 100644
--- a/docs/doc/reverse.html
+++ b/docs/doc/reverse.html
@@ -20,7 +20,7 @@
      ┘
 
      'c'
-ERROR
+Error: ⌽: Argument cannot be a unit
 

You can't reverse an atom or rank-0 array because it has no axes to reverse along, or it could be said no ordering to reverse.

To reverse along an axis other than the first, use Cells (˘) or Rank ().

@@ -62,7 +62,7 @@ ERROR ┘ 2 'c' # No axes to rotate -ERROR +Error: ⌽: 𝕩 must have rank at least 1 for atom 𝕨

Elements are always rotated to the left, so that entry i of the result is entry 𝕨+i of the argument—or rather, entry (𝕩)|𝕨+i to enable elements to cycle around. This can be seen directly by using the range n as an argument: then the value of 𝕩 at index i is just i.

↗️
    2  6
@@ -91,7 +91,7 @@ ERROR
 

Rotate also allows 𝕨 to be a list (or unit array) of integers, in which case they're matched with leading axes of 𝕩. This means the length of 𝕨 can't be larger than the rank of 𝕩, or there wouldn't be enough axes to match. This rule also explains why 𝕩 has to have rank one or more when 𝕨 is an atom, because 𝕨 is treated as the one-element list 𝕨 in that case.

↗️
    342  "just a list"
-ERROR
+Error: 𝕨⌽𝕩: Length of compound 𝕨 must be at most rank of 𝕩
 

The expression below rotates the first (vertical) axis of tab by one element, and second by two. So the line of capital letters goes from being one away from the top, up to the top, and the column with '2' goes from horizontal index 2 to index 0.

↗️
    12  tab
diff --git a/docs/doc/select.html b/docs/doc/select.html
index 919a80c0..aaa36cc3 100644
--- a/docs/doc/select.html
+++ b/docs/doc/select.html
@@ -64,7 +64,7 @@
 "two"
 
     0  <5  # No first axis to select from
-ERROR
+Error: ⊏: 𝕩 cannot be a unit
 

As a major cell of 𝕩, the result has rank one less than it and its shape is 1↓≢𝕩. 𝕩 must have rank one or more.

The index 𝕨 has to be an integer less than 𝕩. It can be negative, in which case it must be greater than or equal to -≠𝕩. Negative indices select from the end of 𝕩, in that ¯1 indicates the last major cell and -≠𝕩 indicates the first. If 𝕩 is 0, then no index is valid.

@@ -74,7 +74,7 @@ ┘ 0 "" -ERROR +Error: ⊏: Indexing out-of-bounds (𝕨≡0, 0≡≠𝕩)

The monadic case First Cell (𝕩) is identical to 0𝕩. It has the same restrictions: 𝕩 must have rank 1 or more, and length 1 or more.

↗️
     "abc"
@@ -89,7 +89,7 @@ ERROR
 "abc"
 
      'a'
-ERROR
+Error: ⊏: Argument cannot be an atom
 

First-axis selection

If 𝕨 is an array of numbers (including any empty array), then each number indicates a major cell of 𝕩. In the simplest case, a list of numbers gives a result with the same rank as 𝕩 but maybe not the same length.

diff --git a/docs/doc/undo.html b/docs/doc/undo.html index 64d8ab53..f2082a10 100644 --- a/docs/doc/undo.html +++ b/docs/doc/undo.html @@ -38,7 +38,7 @@

A few notable inverses are the logarithm , un-Transpose , and Indices inverse /. Enclose inverse, <, is an alternative to First that requires its argument to be a unit array.

Structural functions like Take and shifts that remove elements from 𝕩 can't be inverted, because given the result there's no way to know what the elements should be. However, there are two special cases that have inverses defined despite losing data: these are and k where k is a constant (a data type, or k˙). For these, 𝕩 is required to match the always returned value 𝕨 or k, and this value is also used for the result—even though any result would be valid, as these functions ignore 𝕩.

↗️
    3  4
-ERROR
+Error: ⁼: Inverse does not exist
     3  3
 3
 
diff --git a/docs/style.css b/docs/style.css index 9adb848e..383c2b01 100644 --- a/docs/style.css +++ b/docs/style.css @@ -194,6 +194,7 @@ kbd { .Number { color: #6f251f; } .Comment { color: #32328b; } .String { color: #2d7583; } +.Error { color: #ee3030; } a:link { color: #0b39dc; text-decoration-color: #0b39dc91; } a:visited { color: #3d155f; } @@ -229,6 +230,7 @@ a:visited { color: #3d155f; } .Number { color: #a73227; } .Comment { color: #3f3daa; } .String { color: #3e99ab; } + .Error { color: #a50d0d; } a:link { color: #5592d9; text-decoration-color: #508dd978; } a:visited { color: #8781c1; } diff --git a/docs/tutorial/list.html b/docs/tutorial/list.html index 4df69829..35853585 100644 --- a/docs/tutorial/list.html +++ b/docs/tutorial/list.html @@ -338,7 +338,7 @@

Multiplication is harder, and if we try to multiply by the place value list directly it doesn't go so well.

↗️
    (2⋆↕8) × '0' -˜ "01001110""01100101""01110010""01100100""00100001"
-ERROR
+Error: ×: Expected equal shape prefix (⟨8⟩ ≡ ≢𝕨, ⟨5⟩ ≡ ≢𝕩)
 

This is because the list on the left has length 8 while the list on the right has length 5. The elements of the list on the right have length 8, but BQN can't be expected to know you want to connect the two arguments in that particular way. Especially considering that if you happen to have 8 characters then the right argument will have length 8!

There are a few ways to handle this. What we'll do is bind the place values to × using the 2-modifier . This modifier attaches a left argument to a function.

diff --git a/docs/tutorial/variable.html b/docs/tutorial/variable.html index 634bd5d5..16186db4 100644 --- a/docs/tutorial/variable.html +++ b/docs/tutorial/variable.html @@ -25,7 +25,7 @@

A variable can't be defined twice in the same scope. Later we'll work with functions and other pieces of code that create their own scopes, but for now all you need to know is that all the code in a tutorial runs in the same scope. So three is already defined, and can't be defined again.

↗️
    three  4
-ERROR
+Error: Redefinition
 

It's a little crazy to call them variables if the definition can never change, right? Doesn't "variable" mean "able to change"? Fortunately, this is one way in which BQN isn't crazy. You can modify a variable's value with the arrow provided it's already been defined. This never does anything to the original value: that value stays the same; it's just (probably) not the value of the modified variable any more.

↗️
    three  4
@@ -37,12 +37,12 @@ ERROR
 1
 
     four  3    # four isn't defined yet
-ERROR
+Error: Undefined identifier
 

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

↗️
    BQN  "[many pages of specification]"
-ERROR
+Error: Role of the two sides in assignment must match
 

What's going on? Does BQN not know about capital letters? Does it object to self-reference? Why is "BQN" green?

If you open that statement in the online REPL, you'll see the more informative message "Role of the two sides in assignment must match" (assignment means anything written with a leftward arrow—either definition or modification). This is still cryptic but at least a "role" is something we've heard about before.

@@ -65,7 +65,7 @@ ERROR -3{𝔽} - _three -ERROR +Error: Interpreting non-1-modifier as 1-modifier

Now might be a good time to review the earlier material on roles, experiment, and see if you can puzzle out what's happening here. Or a good time to keep reading until the horrifying distortions these texts inevitably wrap around your existence become apparent, so I'll explain that all these names do represent the same value—they all refer to the same variable—but they have different syntactic roles. Just as the same person might sometimes stand in front of the counter to order a coffee and sometimes stand behind it pouring coffee, the same variable is spelled different ways to indicate what it might be doing right now. There's a spelling for each role:

-- cgit v1.2.3