From 65ac589b7ffd6bfb5b14cb498678d2125ef3431f Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Wed, 22 Dec 2021 16:13:35 -0500 Subject: Finish APL/J comparisons, maybe --- commentary/why.md | 19 +++++++++++++++---- doc/fromDyalog.md | 2 +- doc/fromJ.md | 2 +- docs/commentary/why.html | 17 +++++++++++++---- docs/doc/fromDyalog.html | 2 +- docs/doc/fromJ.html | 2 +- 6 files changed, 32 insertions(+), 12 deletions(-) diff --git a/commentary/why.md b/commentary/why.md index 0956fa63..912ba22d 100644 --- a/commentary/why.md +++ b/commentary/why.md @@ -20,27 +20,38 @@ The major differences are listed on [the front page](../README.md#whats-the-lang In addition to these, BQN's [block system](../doc/block.md) 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! I made a longer argument [here](../doc/arrayrepr.md#why-not-whitespace); 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](../doc/arrayrepr.md#why-not-whitespace); 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. +BQN has no built-in control structures, which can be quite an adjustment coming from certain styles of APL or J. The [control structures](../doc/control.md) 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: +- The index origin is 0. +- APL and J use approximate comparison in primitives, controlled by a value called the comparison tolerance (`⎕CT` in APL). The choice of which primitives use it and how is kind of arbitrary, and a nonzero comparison tolerance can lead to confusion, bugs, and unavoidable performance problems in code. Nonetheless, I planned to add tolerant comparison to BQN—until I realized that after a year spent programming in BQN I'd hardly noticed its absence, and no one had asked for it either. +- Random number generation isn't a primitive, but instead uses the global generator `•rand` or an initialized generator `•MakeRand`. This makes managing independent generators easier, and with namespaces [you get](../spec/system.md#random-generation) several convenient functions for different use cases. + +Some factors specific to APL or J are given in the sections below. ### APL +*See also the [BQN-Dyalog APL dictionary](../doc/fromDyalog.md).* + 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`. BQN adopts [leading axis theory](../doc/leading.md) 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 index 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](primitive.md) 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](../doc/rebqn.md)… -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. Tradfns are removed entirely, along with control structures. +An APL selective assignment `arr[2 3]+←1` should usually be written with Under in BQN: `1⊸+⌾(2‿3⊸⊏)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 `a←1⋄a←2` makes two different variables; in BQN this is an error because the second should use `↩`. Tradfns are removed entirely, along with control structures. 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. ### J -*J is under development again and a moving target. I stopped using it completely shortly after starting work on BQN in 2020, and while I try to keep up to date on language changes, some remarks here might not fit with the experience you'd get starting with J today.* +*See also the [BQN-J dictionary](../doc/fromJ.md). J is under development again and a moving target. I stopped using it completely shortly after starting work on BQN in 2020, and while I try to keep up to date on language changes, some remarks here might not fit with the experience you'd get starting with J today.* To me building with J feels like making a tower out of wood and nails by hand: J itself is reliable but I soon don't trust what I'm standing on. J projects start to feel hacky when I have multiple files, locales, or a bit of global state. With BQN I begin to worry about maintainability only when I have enough functions that I can't remember what arguments they expect, and with lexically-scoped variables I simply don't use global state. If you don't reach this scale (in particular, if you use J as a calculator or spreadsheet substitute) you won't feel these concerns, and will have less to gain by moving to BQN. And if you go beyond, you'd need to augment your programs with rigorous documentation and testing in either language. diff --git a/doc/fromDyalog.md b/doc/fromDyalog.md index a98a2365..712e5e56 100644 --- a/doc/fromDyalog.md +++ b/doc/fromDyalog.md @@ -2,7 +2,7 @@ # BQN–Dyalog APL dictionary -A few tables to help users of Dyalog APL (or similar) get started quickly on BQN. Here we assume `⎕ML` is 1 for Dyalog. +A few tables to help users of Dyalog APL (or similar) get started quickly on BQN. For a higher-level comparison, check [Why BQN?](../commentary/why.md#versus-apl-and-j). Here we assume `⎕ML` is 1 for Dyalog. ## Terminology diff --git a/doc/fromJ.md b/doc/fromJ.md index afc470ab..73a79f9c 100644 --- a/doc/fromJ.md +++ b/doc/fromJ.md @@ -6,7 +6,7 @@ "style" Enc ".Comment { color: inherit; }" --> -A guide to help users of J get up to speed with BQN quickly. +A guide to help users of J get up to speed with BQN quickly. For a higher-level comparison, check [Why BQN?](../commentary/why.md#versus-apl-and-j). ## Terminology diff --git a/docs/commentary/why.html b/docs/commentary/why.html index 48ed791f..77048ae3 100644 --- a/docs/commentary/why.html +++ b/docs/commentary/why.html @@ -14,17 +14,26 @@

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! I made a longer argument 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.

+

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:

+ +

Some factors specific to APL or J are given in the sections below.

APL

+

See also the BQN-Dyalog APL dictionary.

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.

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 index 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

-

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. Tradfns are removed entirely, along with control structures.

+

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

J

-

J is under development again and a moving target. I stopped using it completely shortly after starting work on BQN in 2020, and while I try to keep up to date on language changes, some remarks here might not fit with the experience you'd get starting with J today.

+

See also the BQN-J dictionary. J is under development again and a moving target. I stopped using it completely shortly after starting work on BQN in 2020, and while I try to keep up to date on language changes, some remarks here might not fit with the experience you'd get starting with J today.

To me building with J feels like making a tower out of wood and nails by hand: J itself is reliable but I soon don't trust what I'm standing on. J projects start to feel hacky when I have multiple files, locales, or a bit of global state. With BQN I begin to worry about maintainability only when I have enough functions that I can't remember what arguments they expect, and with lexically-scoped variables I simply don't use global state. If you don't reach this scale (in particular, if you use J as a calculator or spreadsheet substitute) you won't feel these concerns, and will have less to gain by moving to BQN. And if you go beyond, you'd need to augment your programs with rigorous documentation and testing in either language.

The biggest difference could be in file loading. If you write a script that depends on other files, and want it to work regardless of the directory it's called from, you need to deal with this. In J, >{:4!:3 '' gives the name of the most recently loaded script (the current one, if you put it before any imports), but to make it into a utility you need this glob of what's-going-on:

cur_script =: {{(4!:3$0) {::~ 4!:4<'y'}}
diff --git a/docs/doc/fromDyalog.html b/docs/doc/fromDyalog.html
index de77cdf6..0f6a727a 100644
--- a/docs/doc/fromDyalog.html
+++ b/docs/doc/fromDyalog.html
@@ -5,7 +5,7 @@
 
 
 

BQN–Dyalog APL dictionary

-

A few tables to help users of Dyalog APL (or similar) get started quickly on BQN. Here we assume ML is 1 for Dyalog.

+

A few tables to help users of Dyalog APL (or similar) get started quickly on BQN. For a higher-level comparison, check Why BQN?. Here we assume ML is 1 for Dyalog.

Terminology

Array model

BQN uses the based array model, so that a Dyalog simple scalar corresponds to many BQN values: an atom, its enclose, and so on.

diff --git a/docs/doc/fromJ.html b/docs/doc/fromJ.html index 9b40695c..9b053151 100644 --- a/docs/doc/fromJ.html +++ b/docs/doc/fromJ.html @@ -6,7 +6,7 @@

BQN–J dictionary

-

A guide to help users of J get up to speed with BQN quickly.

+

A guide to help users of J get up to speed with BQN quickly. For a higher-level comparison, check Why BQN?.

Terminology

Array model

BQN uses the based array model, which is fundamentally different from J's flat array model. BQN uses non-array values such as characters and numbers, called "atoms", while in J every noun is an array. A BQN array can contain any values in any mixture, while a J array must be uniformly numbers, characters, or boxes (BQN doesn't use boxes).

-- cgit v1.2.3