From 0d6c26b9aa607ff14e14e6488bace207e324022a Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Mon, 13 Jun 2022 13:20:34 -0400 Subject: =?UTF-8?q?Highlight=20[]=20as=20list=20brackets,=20like=20?= =?UTF-8?q?=E2=9F=A8=E2=9F=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/commentary/history.html | 4 ++-- docs/commentary/problems.html | 4 ++-- docs/commentary/why.html | 10 +++++----- docs/doc/array.html | 2 +- docs/doc/arrayrepr.html | 2 +- docs/doc/based.html | 4 ++-- docs/doc/embed.html | 10 +++++----- docs/doc/fromDyalog.html | 4 ++-- docs/doc/fromJ.html | 12 ++++++------ docs/implementation/kclaims.html | 4 ++-- docs/implementation/vm.html | 4 ++-- docs/spec/system.html | 2 +- docs/tutorial/variable.html | 2 +- md.bqn | 4 ++-- 14 files changed, 34 insertions(+), 34 deletions(-) diff --git a/docs/commentary/history.html b/docs/commentary/history.html index d1063fdd..f6955a71 100644 --- a/docs/commentary/history.html +++ b/docs/commentary/history.html @@ -208,12 +208,12 @@

Prefix, Suffix, and Windows

I discovered Prefix, Suffix, and Windows while thinking about Iridescence, probably in 2019. They are influenced by J's Prefix, Suffix, and Infix operators, but in Iridescence, with no distinction between functions and arrays, Prefix is just the Take function, and Suffix is Drop!

Array notation

-

APL array notation has been developed mainly by Phil Last and later Adám Brudzewsky. The big difference from array literals in other languages is the idea that newline should be a separator equivalent to , as it is in ordinary APL execution including dfns. The changes I made for BQN, other than the ligature discussed below, were to use dedicated bracket pairs ⟨⟩ and [], and to allow , as a separator.

+

APL array notation has been developed mainly by Phil Last and later Adám Brudzewsky. The big difference from array literals in other languages is the idea that newline should be a separator equivalent to , as it is in ordinary APL execution including dfns. The changes I made for BQN, other than the ligature discussed below, were to use dedicated bracket pairs ⟨⟩ and [], and to allow , as a separator.

I picked out the ligature character between YAG meetings, but I think Richard Park was most responsible for the idea of a "shortcut" list notation.

Double-struck special names

There was a lot of discussion about names for arguments at YAG (no one liked alpha and omega); I think Nathan Rogers suggested using Unicode's mathematical variants of latin letters and I picked out the double-struck ones. My impression is that we were approaching a general consensus that "w" and "x" were the best of several bad choices of argument letters, but that I was the first to commit to them.

Assert primitive

-

Nathan Rogers suggested that assertion should be made a primitive to elevate it to a basic part of the language. I used J's assert often enough for this idea to make sense immediately, but I think it was new to me. He suggested the dagger character; I changed this to the somewhat similar-looking !. The error-trapping modifier is identical to J's ::, but J only has the function [: to unconditionally throw an error, with no way to set a message.

+

Nathan Rogers suggested that assertion should be made a primitive to elevate it to a basic part of the language. I used J's assert often enough for this idea to make sense immediately, but I think it was new to me. He suggested the dagger character; I changed this to the somewhat similar-looking !. The error-trapping modifier is identical to J's ::, but J only has the function [: to unconditionally throw an error, with no way to set a message.

Context-free grammar

In YAG meetings, I suggested adopting APL\iv's convention that variable case must match variable type in order to achieve a context-free grammar. Adám, a proponent of case-insensitive names, pointed out that the case might indicate the type the programmer wanted to use instead of the value's type, creating cross roles. Although I considered swapping subjects and functions, I ended up using exactly the conventions of his APL style guide.

Headers

diff --git a/docs/commentary/problems.html b/docs/commentary/problems.html index 38d41ea9..2937be17 100644 --- a/docs/commentary/problems.html +++ b/docs/commentary/problems.html @@ -35,7 +35,7 @@

Right-to-left multi-line functions go upwards

If you include multiple multi-line functions in what would otherwise be a one-liner, the flow in each function goes top to bottom but the functions are executed bottom to top. I think the fix in BQN is to just say give your functions names and don't do this. But left to right programming beckons.

Trains don't like monads

-

If you have the normal mix of monads and dyads you'll need a lot of parentheses and might end up abusing . Largely solved with the Nothing syntax ·, which acts like J's Cap ([:) in a train, but still a minor frustration.

+

If you have the normal mix of monads and dyads you'll need a lot of parentheses and might end up abusing . Largely solved with the Nothing syntax ·, which acts like J's Cap ([:) in a train, but still a minor frustration.

Under/bind combination is awkward

It's most common to use Under with dyadic structural functions in the form (iF), for example where F is one of / or . This is frustrating for two reasons: it requires parentheses, and it doesn't allow i to be computed tacitly. If there's no left argument then the modifier {𝔽(𝕨𝔾)𝕩} can be more useful, but it doesn't cover some useful cases such as mask a (u/) b. Another form of Under that's sometimes wanted is {𝕨𝔽𝔾𝕩}. One modifier can only do so much.

List splicing is fiddly

@@ -102,7 +102,7 @@

Monadic argument corresponds to left for / and

Called dyadically, both functions shuffle cells of the right argument around, which is consistent with other selection-type functions. But the monadic case applies to what would be the left argument in the dyadic case.

High-rank array notation awkwardness

-

The notation [] will be added for high-rank arrays, the same as BQN's lists ⟨⟩ except it mixes at the end. It looks okay with BQN strands but clashes with BQN lists. At that point it becomes apparent that specifying whether something is a high-rank array at the top axes is kind of strange: shouldn't it be the lower axes saying to combine with higher ones? A more concrete point of awkwardness is that literal notations can only form arrays with rank 1 or more, preventing unit arrays from being destructured. Syntax with < and [] would be complete over non-empty arrays.

+

The notation [] will be added for high-rank arrays, the same as BQN's lists ⟨⟩ except it mixes at the end. It looks okay with BQN strands but clashes with BQN lists. At that point it becomes apparent that specifying whether something is a high-rank array at the top axes is kind of strange: shouldn't it be the lower axes saying to combine with higher ones? A more concrete point of awkwardness is that literal notations can only form arrays with rank 1 or more, preventing unit arrays from being destructured. Syntax with < and [] would be complete over non-empty arrays.

Assert has no way to compute the error message

In the compiler, error messages could require expensive diagnostics, and in some cases the message includes parts that can only be computed if there's an error (for example, the index of the first failure). However, Assert (!) only takes a static error message, so you have to first check a condition, then compute the message, then call Assert on that. Kind of awkward, but better than it used to be before one-argument Assert was changed to use 𝕩 for the message. The issue generally applies to high-quality tools built in BQN, where giving the user good errors is a priority.

Monadic versus >

diff --git a/docs/commentary/why.html b/docs/commentary/why.html index 158dc679..9ab7e0a0 100644 --- a/docs/commentary/why.html +++ b/docs/commentary/why.html @@ -14,7 +14,7 @@

BQN is more like APL, but adopts some of the developments made by J as well. However, it's much simpler than both, with fewer and less overloaded primitives as well as less special syntax (J has fewer syntactic rules, but more special cases handled during execution that I think should have been implemented with syntax).

The major differences are listed on the front page ("But it's redesigned…"): based arrays, list notation, context-free grammar and first-class functions, reworked primitives, and dedicated namespace syntax.

In addition to these, BQN's block system extends APL dfns with headers, adding some very useful functionality: the header specifies block type and argument names, and also allows for simple pattern matching when used with multiple block bodies.

-

Since this section gets into the details, it's worth highlighting stranding, a feature I think of as an obvious improvement but that many BQN newcomers see as an obvious sign that I don't know what I'm doing! My full argument for this decision is here; the two key points are that stranding is a source of ambiguity that can strike at any time, requiring a correction with or ], and that typing is really not hard I promise.

+

Since this section gets into the details, it's worth highlighting stranding, a feature I think of as an obvious improvement but that many BQN newcomers see as an obvious sign that I don't know what I'm doing! My full argument for this decision is here; the two key points are that stranding is a source of ambiguity that can strike at any time, requiring a correction with or ], and that typing is really not hard I promise.

BQN's heavier-weight ⟨⟩ syntax for lists also has its own advantages, because it can be formatted nicely across multiple lines, and also allows functions and modifiers to be used easily as elements. Being able to easily map over a list of functions is surprisingly useful!

BQN has no built-in control structures, which can be quite an adjustment coming from certain styles of APL or J. The control structures page gives some ways to write in a more imperative style, but it's definitely not the same.

Primitives in BQN are pure functions that don't depend on interpreter settings. The following kinds of interpreter state don't apply:

@@ -27,10 +27,10 @@

APL

See also the BQN-Dyalog APL dictionary. I compare to Dyalog here as it's the most widely used dialect.

BQN cleans up some awkward syntax left over from when each APL operator was special: the outer product is written Fn rather than .fn, and reduction Fn´ arr is separated from compress b/arr instead of overloading.

-

BQN adopts leading axis theory as developed in SHARP APL and applied in A+ and J. With this it can collapse APL pairs such as and / to one primitive each, and remove APL's complicated function axis (such as [2]) mechanism. The Rank modifier then applies these primitives to non-leading axes. While this method is required in J and also favored by many users of Dyalog APL, it definitely doesn't enjoy universal support—it can be harder to learn, and less convenient for some common cases. Summing rows with +/ in APL is quite convenient, and BQN's +˝1, or +˝˘ for matrices, just aren't as nice.

+

BQN adopts leading axis theory as developed in SHARP APL and applied in A+ and J. With this it can collapse APL pairs such as and / to one primitive each, and remove APL's complicated function axis (such as [2]) mechanism. The Rank modifier then applies these primitives to non-leading axes. While this method is required in J and also favored by many users of Dyalog APL, it definitely doesn't enjoy universal support—it can be harder to learn, and less convenient for some common cases. Summing rows with +/ in APL is quite convenient, and BQN's +˝1, or +˝˘ for matrices, just aren't as nice.

Arguably BQN cuts down the set of primitives too much. Base conversion ⊥⊤, partitioning ⊂⊆, and matrix division are commonly asked-for primitives, but they don't match my conception of a primitive. And while each can be implemented (with short snippets, other than which requires a library), there's definitely a convenience loss. But there's always ReBQN

BQN's Power modifier allows an array operand to specify multiple results, for example Fn(4) to get 0 up to 3 iterations. Intermediate results are saved, so the number of calls only depends on the highest iteration number present. On the other hand, BQN has no direct equivalent of Power Limit , requiring it to be implemented manually.

-

An APL selective assignment arr[2 3]+1 should usually be written with Under in BQN: 1+(23)arr (but the correspondence might not always be so direct). You can think of this as a very fancy At (@) operator, that lets you pull out an arbitrary part of an array.

+

An APL selective assignment arr[2 3]+1 should usually be written with Under in BQN: 1+(23)arr (but the correspondence might not always be so direct). You can think of this as a very fancy At (@) operator, that lets you pull out an arbitrary part of an array.

Dfns are adjusted in a few ways that make them more useful for general-purpose programming. A BQN block always runs to the last statement, so a block like {Update 𝕩 1+x} won't return early. Writing modification with makes it clearer which variable's which. Dfns also do a weird shadowing thing where a1a2 makes two different variables; in BQN this is an error because the second should use . Tradfns are removed entirely, along with control structures.

BQN doesn't have an exact replacement for dfn guards, although the predicate ? can look similar: {2| : 1+3× ÷2} is equivalent to {2|𝕩 ? 1+3×𝕩 ; 𝕩÷2}. But note that where APL uses the statement separator , BQN uses the body separator ;. This means that the if-true branch in BQN can consist of multiple statements (including additional predicates), but also that the if-false branch can't access variables defined in or before the condition. In both cases the "better" behavior can be obtained with an extra set of braces and possibly assigning names to arguments /𝕩. I think guards end up being cleaner when they work, and predicates are more versatile.

BQN's namespaces have a dedicated syntax, are much easier to create than Dyalog namespaces, and have better performance. I use them all the time, and they feel like a natural part of the language.

@@ -46,9 +46,9 @@

But J has its type advantages as well. I miss complex number support in BQN, as it's an optional extension that we haven't yet implemented. And BQN has a hard rule that only one numeric type is exposed to the programmer, which means high-precision integers and rationals aren't allowed at all for a float-based implementation. I think this rule is worth it because J's implicit type conversion is hard to predict and an unexpected numeric type can cause sporadic or subtle program errors.

BQN uses a modifier for J's hook, adding for a reversed version (which I use nearly twice as often). This frees up the 2-train, which is made equivalent to Atop (). It's the system Roger Hui came to advocate, since he argued in favor of a hook conjunction here and made 2-train an Atop when he brought it to Dyalog APL. As an example, the J hook (#~0&<:) to remove negative numbers becomes 0/ in BQN. Hooks are also the topic of Array Cast episode 14, where the panel points out that in J, adding a verb at the far left of a dyadic train changes the rest of the train from dyadic to monadic or vice-versa, an effect that doesn't happen in BQN.

J locales are not first-class values, and BQN namespaces are. I think BQN's namespaces are a lot more convenient to construct, although it is lacking an inheritance mechanism (but J's path system can become confusing quickly). More importantly, BQN namespaces (and closures) are garbage collected. J locales leak unless manually freed by the programmer. More generally, J has no mutable data at all, and to simulate it properly you'd have to write your own tracing garbage collection as the J interpreter doesn't have any. I discussed this issue some in this J forum thread.

-

In J, each function has a built-in rank attribute: for example the ranks of + are 0 0 0. This rank is accessed by the "close" compositions @, &, and &.. Choosing the shorter form for the close compositions—for example @ rather than @:—is often considered a mistake within the J community. And function ranks are unreliable: consider that the ranks of ]@:+, a function that always has the same result as +, are _ _ _. In BQN there aren't any close compositions at all, and no function ranks. J's &.> is simply ¨, and other close compositions, in my opinion, just aren't needed.

+

In J, each function has a built-in rank attribute: for example the ranks of + are 0 0 0. This rank is accessed by the "close" compositions @, &, and &.. Choosing the shorter form for the close compositions—for example @ rather than @:—is often considered a mistake within the J community. And function ranks are unreliable: consider that the ranks of ]@:+, a function that always has the same result as +, are _ _ _. In BQN there aren't any close compositions at all, and no function ranks. J's &.> is simply ¨, and other close compositions, in my opinion, just aren't needed.

J has several adverbs (key, prefix, infix, outfix…) to slice up an argument in various ways and apply a verb to those parts. In BQN, I rejected this approach: there are 1-modifiers for basic iteration patterns, and functions such as Group () that do the slicing but don't apply anything. So </.~a is a, but fn/.~a is >Fn¨a (I also reject J's implicit merge except for the Rank modifier, as I don't think function results should be homogeneous by default). BQN's approach composes better, and is more predictable from a performance perspective.

-

Gerunds are J's answer to BQN's first-class functions. For example J's (%&2)`(1+3*])@.(2&|) would be written 2|÷2,1+3×⊢ with a list of functions. I think lists of functions are a big improvement, since there's no need to convert between gerund and function, and no worries about arrays that just happen to be valid gerunds (worried about losing the ability to construct gerunds? Constructing tacit functions in BQN is much easier). The usability gap widens because passing J functions around either as values or gerunds has presents some highly idiosyncratic challenges, discussed below.

+

Gerunds are J's answer to BQN's first-class functions. For example J's (%&2)`(1+3*])@.(2&|) would be written 2|÷2,1+3×⊢ with a list of functions. I think lists of functions are a big improvement, since there's no need to convert between gerund and function, and no worries about arrays that just happen to be valid gerunds (worried about losing the ability to construct gerunds? Constructing tacit functions in BQN is much easier). The usability gap widens because passing J functions around either as values or gerunds has presents some highly idiosyncratic challenges, discussed below.

Named functions

Its impact on the programmer is smaller than a lot of the issues above, but this section describes a behavior that I find pretty hard to justify. What does the identifier fn indicate in a J expression? The value of fn in the current scope, one might suppose. Nope—only if the value is a noun. Let's make it a function.

   fn =: -
diff --git a/docs/doc/array.html b/docs/doc/array.html
index ec4eab33..827f05c0 100644
--- a/docs/doc/array.html
+++ b/docs/doc/array.html
@@ -6,7 +6,7 @@
 
 

The array

As BQN is an array language, it's often helpful to understand what an array is when writing BQN programs. Fully describing the concept is sometimes held to be tricky; here we'll see definitions, examples, and metaphors.

-

In BQN, as in APL, arrays are multidimensional, instead of strictly linear. Languages like Python, Javascript, or Haskell offer only one-dimensional arrays with [] syntax, and typically represent multidimensional data with nested arrays. Multidimensional arrays have fundamental differences relative to this model.

+

In BQN, as in APL, arrays are multidimensional, instead of strictly linear. Languages like Python, Javascript, or Haskell offer only one-dimensional arrays with [] syntax, and typically represent multidimensional data with nested arrays. Multidimensional arrays have fundamental differences relative to this model.

BQN's arrays are immutable, meaning that an array is entirely defined by its attributes, and there is no way to modify an existing array, only to produce another array that has changes relative to it. As a result, an array can never contain itself, and arrays form an inductive type. BQN's mutable types are operations and namespaces.

An array might also have a fill element that captures some structural information about its elements and is used by a few operations. The fill, as an inferred property, isn't considered to truly be part of the array but is instead some information about the array that the interpreter keeps track of. So it's out of scope here.

diff --git a/docs/doc/arrayrepr.html b/docs/doc/arrayrepr.html index 0e59315a..ce74c543 100644 --- a/docs/doc/arrayrepr.html +++ b/docs/doc/arrayrepr.html @@ -229,4 +229,4 @@ 0 5 ┘
-

The characters [] are reserved to potentially combine list notation with merging, allowing the above to be written [23, 41, 05]. This would allow non-empty arrays with rank one or more to be written without a primitive, but not rank 0 or empty arrays. Since creating arrays in general would still require primitives like < or , it's not clear whether this notation is worth it. General array notation is a surprisingly complicated topic; see the article about it on the APL Wiki.

+

The characters [] are reserved to potentially combine list notation with merging, allowing the above to be written [23, 41, 05]. This would allow non-empty arrays with rank one or more to be written without a primitive, but not rank 0 or empty arrays. Since creating arrays in general would still require primitives like < or , it's not clear whether this notation is worth it. General array notation is a surprisingly complicated topic; see the article about it on the APL Wiki.

diff --git a/docs/doc/based.html b/docs/doc/based.html index 7974f1d5..f03adf29 100644 --- a/docs/doc/based.html +++ b/docs/doc/based.html @@ -43,10 +43,10 @@

Arrays in BQN, like nearly all data structures in modern programming languages, are an inductive type. That means that an array can be constructed from existing values, but can't contain itself (including recursively: an array always has finite depth). To construct the type of all BQN values inductively, we would say that atoms form the base case, and arrays are an inductive case: an array is a shaped collection of existing BQN values. For an array programmer, this is of course the easy part.

Versus the nested array model

The nested array model of NARS, APL2, Dyalog, and GNU APL can be constructed from the based model by adding a rule: a unit (or "scalar" in APL) array containing an atom is equivalent to that atom. The equivalents of atoms in nested array theory are thus called "simple scalars", and they are considered arrays but share the characteristics of BQN atoms. Nested arrays don't form an inductive type, because simple scalars contain themselves.

-

Nested array theory can seem simpler to use, because the programmer never has to worry about simple scalars being enclosed the wrong number of times: all these encloses have been identified with each other. For example, 'abcd'[2] returns a character while BQN's 2"abcd" returns an array containing a character. However, these issues usually still appear with more complex arrays: 'ab' 1 'ef'[2] (here spaces are used for stranding) is not a string but an enclosed string!

+

Nested array theory can seem simpler to use, because the programmer never has to worry about simple scalars being enclosed the wrong number of times: all these encloses have been identified with each other. For example, 'abcd'[2] returns a character while BQN's 2"abcd" returns an array containing a character. However, these issues usually still appear with more complex arrays: 'ab' 1 'ef'[2] (here spaces are used for stranding) is not a string but an enclosed string!

A property that might warn about dangerous issues like this is that nested array theory tends to create inversions where the depth of a particular array depends on its rank (reversing the normal hierarchy of depth→rank→shape). A 1-character string has depth 1, but when its rank is reduced to 0, its depth is reduced as well.

In some cases nested array theory can remove a depth issue entirely, and not just partially. Most notable is the search function result depth issue, in which it's impossible for a search function in BQN to return an atomic number because it always returns an array. Nested array theory doesn't have this issue since a scalar number is "just a number", and more complicated arrays can't cause problems because a search function's result is always a numeric array. The other half of the problem, about the non-principal argument depth, is only partly hidden, and causes problems for example when searching for a single string out of a list of strings.

Versus the boxed array model

The boxed array model of SHARP APL, A+, and J is an inductive system like BQN's. But this model uses arrays as the base case: numeric and character arrays are the simplest kind of data allowed, and "a number" means a rank-0 numeric array. The inductive step is the array of boxes; as with numbers "a box" is simply a rank-0 array of boxes.

-

Numeric and character arrays in this system have depth 0. In general these correspond to arrays of depth 1 in BQN, but because there's no lower depth they are also used where BQN atoms would appear. For example, both Shape ($) and Length (#) return depth-0 results in J. For an array a with rank at least 1, the length #a is exactly [/ $ a, while the identical BQN code ˝ a returns not a but < a. Like the nested model, the boxed model can hide depth issues that occur at lower depths but generally reveals them at higher depths.

+

Numeric and character arrays in this system have depth 0. In general these correspond to arrays of depth 1 in BQN, but because there's no lower depth they are also used where BQN atoms would appear. For example, both Shape ($) and Length (#) return depth-0 results in J. For an array a with rank at least 1, the length #a is exactly [/ $ a, while the identical BQN code ˝ a returns not a but < a. Like the nested model, the boxed model can hide depth issues that occur at lower depths but generally reveals them at higher depths.

The boundary at depth 0 will tend to cause inconsistencies and confusion in any array language, and boxed array languages push this boundary up a level. This leads to the programmer spending more effort managing boxes: for example, to reverse each list in a list of lists, the programmer can use reverse under open, |. &. >. But to find the lengths of each of these lists, # &. > would yield a boxed list, which is usually not wanted, so # @ > is needed instead. BQN shows that a system that doesn't require these distinctions is possible, as a BQN programmer would use ¨ and ¨.

diff --git a/docs/doc/embed.html b/docs/doc/embed.html index b02342cc..b911046d 100644 --- a/docs/doc/embed.html +++ b/docs/doc/embed.html @@ -16,13 +16,13 @@ i40 {i+𝕩»i} `); -push(3); // [3,0,0,0] -push(-2); // [1,3,0,0] -push(4); // [5,4,3,0] +push(3); // [3,0,0,0] +push(-2); // [1,3,0,0] +push(4); // [5,4,3,0]

Note that this program doesn't have any outer braces. It's only run once, and it initializes i and returns a function. Just putting braces around it wouldn't have any effect—it just changes it from a program that does something to a program that runs a block that does the same thing—but adding braces and using 𝕨 or 𝕩 inside them would turn it into a function that could be run multiple times to create different closures. For example, pushGen = bqn("{i←4⥊𝕩⋄{i+↩𝕩»i}}") causes pushGen(n) to create a new closure with i initialized to 4n.

The program also returns only one function, which can be limiting. But it's possible to get multiple closures out of the same program by returning a list of functions. For example, the following program defines three functions that manipulate a shared array in different ways.

-
let [rotx, roty, flip] = bqn(`
+
let [rotx, roty, flip] = bqn(`
     a  32⥊↕6
     RotX  {a𝕩˘a}
     RotY  {a𝕩a}
@@ -35,7 +35,7 @@
 

JS encodings

In the programs above we've used numbers and functions of one argument, which mean the same thing in BQN and JS. This isn't the case for all types: although every BQN value is stored as some JS value, the way it's represented may not be obvious and there are many JS values that don't represent any BQN value and could cause errors. BQN operations don't verify that their inputs are valid BQN values (this would have a large performance cost), so it's up to the JS programmer to make sure that values passed in are valid. To do this, you need to know the encodings for each of the seven BQN types you're going to use.

The two atomic data values are simple: numbers are just JS numbers, and characters are strings containing a single code point. Arrays are JS arrays, but with some extra information. Since JS arrays are 1-dimensional, a BQN array a is stored as the element list a. Its shape a, a list of numbers, is a.sh in JS (the shape isn't necessarily a BQN array so it doesn't have to have a sh property). Optionally, its fill element is a.fill. Note that a BQN string is not a JS string, but instead an array of BQN characters, or JS strings. To convert it to a JS string you can use str.join("").

-

There are two utilities for converting from JS to BQN data: list([…]) converts a JS array to a BQN list, and str("JS string") converts a string.

+

There are two utilities for converting from JS to BQN data: list([]) converts a JS array to a BQN list, and str("JS string") converts a string.

Operations are all stored as JS functions, with one or two arguments for the inputs. The type is determined by the .m property, which is 1 for a 1-modifier and 2 for a 2-modifier, and undefined or falsy for a function. Functions might be called with one or two arguments. In either case, 𝕩 is the first argument; 𝕨, if present, is the second. Note that F(x,w) in JS corresponds to w F x in BQN, reversing the visual ordering of the arguments! For modifiers there's no such reversal, as 𝕗 is always the first argument, and for 2-modifiers 𝕘 is the second argument. As in BQN, a modifier may or may not return a function.

Operations may have some extra properties set that aren't terribly important for the JS programmer: for each primitive p, p.glyph gives its glyph, and for a compound operation o such as a train, or a modifier with bound operands, o.repr() decomposes it into its parts. It wouldn't make sense to define either of these properties for a function created in JS.

Other functionality

diff --git a/docs/doc/fromDyalog.html b/docs/doc/fromDyalog.html index 1f256416..ea036ec9 100644 --- a/docs/doc/fromDyalog.html +++ b/docs/doc/fromDyalog.html @@ -133,8 +133,8 @@ Monad * *(÷2) -[] -[] +[] +[] ~ ⍤⍴ diff --git a/docs/doc/fromJ.html b/docs/doc/fromJ.html index 524ec8d3..338a17d5 100644 --- a/docs/doc/fromJ.html +++ b/docs/doc/fromJ.html @@ -96,7 +96,7 @@ -[: +[: · Cap @@ -138,8 +138,8 @@ >. <: >: -[ -] +[ +] @@ -231,7 +231,7 @@ Dyad {. }. -]\ +]\ #@]{., -@#@]{.,~ # @@ -375,8 +375,8 @@ %: <. >. -[ -] +[ +] |. |: diff --git a/docs/implementation/kclaims.html b/docs/implementation/kclaims.html index c92318c3..670187b3 100644 --- a/docs/implementation/kclaims.html +++ b/docs/implementation/kclaims.html @@ -38,7 +38,7 @@

cycles is the total number of CPU cycles run. L1-dcache-load-misses shows L1 data cache misses and L1-icache-load-misses shows the instruction cache misses; cache-misses shows accesses that miss every layer of caching, which is a subset of those two (more detailed explanation here). icache_16b.ifdata_stall is a little fancy. Here's the summary given by perf list:

  icache_16b.ifdata_stall
-       [Cycles where a code fetch is stalled due to L1 instruction cache miss]
+       [Cycles where a code fetch is stalled due to L1 instruction cache miss]
 

That's just the whole cost (in cycles) of L1 misses, exactly what we want! First I'll run this on a J program I have lying around, building my old Honors thesis with JtoLaTeX.

 Performance counter stats for 'jlatex document.jtex nopdf':
@@ -73,7 +73,7 @@
        2.138414849 seconds time elapsed
 

And the Python-based font tool I use to build font samples for this site:

-
 Performance counter stats for 'pyftsubset […more stuff]':
+
 Performance counter stats for 'pyftsubset […more stuff]':
 
        499,025,775      cycles:u
         24,869,974      icache_16b.ifdata_stall:u
diff --git a/docs/implementation/vm.html b/docs/implementation/vm.html
index ba96ac12..7a0093db 100644
--- a/docs/implementation/vm.html
+++ b/docs/implementation/vm.html
@@ -43,7 +43,7 @@
 

The starting index refers to the position in bytecode where execution starts in order to evaluate the block. Different bodies will always have the same set of special names, but the variables they define are unrelated, so of course they can have different counts. The given number of variables includes special names, but list of names and export mask don't.

The program's symbol list is included in the tokenization information t: it is 02t. Since the entire program (the source code passed in one compiler call) uses this list, namespace field accesses can be performed with indices alone within a program. The symbol list is needed for cross-program access, for example if •BQN returns a namespace.

Instructions

-

The following instructions are defined (those without names are tentatively reserved only). The ones emitted by the self-hosted BQN compiler are marked in the "used" column. Only those marked "X" are needed to support the compiler and self-hosted runtime. "NS" indicates instructions used only in programs with namespaces, "HE" is for headers : or predicates ?, and "HR" is for high-rank array notation [].

+

The following instructions are defined (those without names are tentatively reserved only). The ones emitted by the self-hosted BQN compiler are marked in the "used" column. Only those marked "X" are needed to support the compiler and self-hosted runtime. "NS" indicates instructions used only in programs with namespaces, "HE" is for headers : or predicates ?, and "HR" is for high-rank array notation [].

@@ -134,7 +134,7 @@ - + diff --git a/docs/spec/system.html b/docs/spec/system.html index 4fe4dd89..67af11c2 100644 --- a/docs/spec/system.html +++ b/docs/spec/system.html @@ -447,7 +447,7 @@

Beginning with the type declarations themselves, a number such as f32 corresponds to a C type with the given quality (i for signed integer, u for unsigned, f for floating-point) and width in bits. The corresponding BQN value is a number, and should be converted exactly for integers and with rounding for decreasing-type conversions. For conversions to or from an integer type, attempting to convert a value to a type that can't contain it, or one outside of the exactly representable integer range (-253 to 253 for IEEE doubles), results in an error.

A pointer such as *u8 comes from a BQN list. If the symbol & is used rather than *, the pointer is called mutable and its contents after the function call completes are also returned as an element of the result. If there is any mutable pointer, the result is a list, unless the result type is "&", in which case there must be exactly one mutable pointer and the result is its value alone. These prefixes can only be used in arguments, meaning that a BQN value is provided, and this value determines the length of both the input and the mutable result.

The letter a indicates that a BQN value is to be passed directly, interpreted in whatever way makes sense for the implementation. A plain * indicates an opaque pointer, to be mapped to a BQN value of namespace type. The behavior of this value is not yet specified. The array and struct types indicate C structs and arrays, and correspond to BQN lists.

-

The bqn value in a conv term indicates a BQN element type to be used. It can be appear after the whole type, or any member of a struct, and applies to the final component (that is, type term) of the type and one preceding *, &, or [n] if present (if a type ends in **, it applies to both *s). This portion of the type corresponds to a BQN list of the given element type, interpreted much like bitwise conversion •bit._cast. The C type is treated as pure data, a stream of bits. For a prefix * or &, the data in question is the region of memory pointed to.

+

The bqn value in a conv term indicates a BQN element type to be used. It can be appear after the whole type, or any member of a struct, and applies to the final component (that is, type term) of the type and one preceding *, &, or [n] if present (if a type ends in **, it applies to both *s). This portion of the type corresponds to a BQN list of the given element type, interpreted much like bitwise conversion •bit._cast. The C type is treated as pure data, a stream of bits. For a prefix * or &, the data in question is the region of memory pointed to.

Operation properties

HR Merge top of stack (for [])Merge top of stack (for [])
10
diff --git a/docs/tutorial/variable.html b/docs/tutorial/variable.html index df0d7c74..1bb916ac 100644 --- a/docs/tutorial/variable.html +++ b/docs/tutorial/variable.html @@ -223,7 +223,7 @@ ↗️
    8 "BQN"        # Change the first element to 8
 ⟨ 8 'Q' 'N' ⟩
 
-

BQN doesn't have a dedicated syntax such as list[index] to select from a list, because a function is more consistent with the rest of BQN's notation and can be manipulated more easily. This decision has already been useful to us, because Under's right operand is a function! With a special notation we'd have to first "package" index selection into a function to use it.

+

BQN doesn't have a dedicated syntax such as list[index] to select from a list, because a function is more consistent with the rest of BQN's notation and can be manipulated more easily. This decision has already been useful to us, because Under's right operand is a function! With a special notation we'd have to first "package" index selection into a function to use it.

diff --git a/md.bqn b/md.bqn index d8a514bf..f3393d8d 100644 --- a/md.bqn +++ b/md.bqn @@ -215,7 +215,7 @@ Markdown ← {filename𝕊𝕩: addRslt ← ∧´ ' ' = ∾ 4 (⌊⟜≠ ↑ ⊢)¨ 𝕩 # Don't show assignment results by default ShowRslt ← { - depth ← +` "(){}⟨⟩" (⊣(≠⊸>ׯ1⋆2|⊢)⊐) 𝕩 + depth ← +` "(){}⟨⟩[]" (⊣(≠⊸>ׯ1⋆2|⊢)⊐) 𝕩 𝕩 /˜↩ ¬ ∨`⌾⌽ (0=depth) ∧ (∧`𝕩≠'#') ∧ 𝕩∊"⋄," # Just the last statement g ← 𝕩∊"←↩" (⊑g⊐1) (<⟜(≠g))◶⟨1,¬(" "∾∾idChars)∧´∘∊˜↑⟩ 𝕩 @@ -565,7 +565,7 @@ hlchars‿classTag ← { "Number" , ∾idChars # Will be classified among ↑↑ later "Gets" , "←⇐↩→" "Paren" , "()" - "Bracket" , "⟨⟩" + "Bracket" , "⟨⟩[]" "Brace" , "{}" "Head" , ":;?" "Ligature" , "‿" -- cgit v1.2.3