From a25cb2b0bf26033c9bc778d816618a752d015d99 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Tue, 5 Jul 2022 16:46:42 -0400 Subject: Somehow, all the docs have now been edited --- docs/commentary/why.html | 20 +++++++-------- docs/doc/fromDyalog.html | 45 +++++++++++++++++++++++---------- docs/doc/fromJ.html | 16 ++++++------ docs/doc/functional.html | 12 ++++----- docs/doc/glossary.html | 58 +++++++++++++++++++++--------------------- docs/doc/group.html | 66 +++++++++++++++++++++++++----------------------- docs/doc/map.html | 28 ++++++++++++-------- 7 files changed, 136 insertions(+), 109 deletions(-) (limited to 'docs') diff --git a/docs/commentary/why.html b/docs/commentary/why.html index 9ab7e0a0..1fe07371 100644 --- a/docs/commentary/why.html +++ b/docs/commentary/why.html @@ -11,9 +11,9 @@

BQN has no intention of being the last word in programming, but could be a practical and elegant tool in your kit—even if only used to inform your use of another language. Give it a try!

Versus APL and J

Here are some more specific comparisons against the two most similar languages to BQN. I'll try to bring up the areas where BQN can be considered worse, but my focus here is definitely on BQN's strong points—I'm not trying to offer an unbiased account.

-

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

+

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 designed as 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.

+

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

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.

@@ -21,7 +21,7 @@

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

APL

@@ -29,10 +29,10 @@

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.

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.

+

BQN's version of the 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.

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

J

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.

@@ -40,15 +40,15 @@

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

In BQN it's •path. And usually you don't need it because •Import resolves paths relative to the file containing it.

+

In BQN it's •path. And usually you don't need it because •Import resolves paths relative to the file containing it (if you want to use the shell's current directory, you have to use •wdpath explicitly).

J uses numeric codes; BQN uses mostly names. So J's 1&o. is BQN's •math.Sin, and 6!:9 corresponds to BQN's •MonoTime.

J uses bytestrings by default, making Unicode handling a significant difficulty (see u:). BQN strings are lists of codepoints, so you don't have to worry about how they're encoded or fight to avoid splitting up UTF-8 bytes that need to go together.

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.

+

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. J has no mutable data at all; to simulate it properly you'd have to write your own tracing garbage collector, as the J interpreter doesn't have one.

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.

+

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 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 assumed 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). Unrelated to these fundamental issues, passing J functions around either as values or gerunds presents some 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/fromDyalog.html b/docs/doc/fromDyalog.html
index ea036ec9..1e36dada 100644
--- a/docs/doc/fromDyalog.html
+++ b/docs/doc/fromDyalog.html
@@ -35,8 +35,7 @@
 
 
 
-

BQN shares the terms "cell" and "major cell" with Dyalog, and uses -"element" (which may mean different things to different Dyalog users) not for a 0-cell but for the value it contains.

+

BQN shares the terms "cell" and "major cell" with Dyalog, and uses "element" (which may mean different things to different Dyalog users) not for a 0-cell but for the value it contains.

Roles

Dyalog uses value types (array, function, and so on) to determine syntax while BQN uses a separate concept called syntactic roles. See context-free grammar.

@@ -71,7 +70,7 @@

Syntax

BQN comments are written with #, not . BQN strings use double quotes "" while single quotes '' enclose a character.

-

BQN's functions use {}, like Dyalog's dfns. The names for inputs and self-reference are different:

+

BQN's block functions use {}, like Dyalog's dfns. The names for inputs and self-reference are different:

@@ -106,11 +105,11 @@
-

BQN doesn't have guards: it uses modifiers or control structures instead. However, BQN function and modifier blocks have headers that allow pattern matching. See the block documentation.

+

Blocks don't have guards exactly, but headers and predicates support some similar functionality (first-class functions can also be used for control structures). Headers can also be used to make a block more explicit about its inputs, more like a tradfn.

The assignment arrow defines a new variable in a block, while modifies an existing one.

-

BQN uses the ligature character for stranding, instead of plain juxtaposition. It also has a list notation using ⟨⟩.

+

BQN uses the ligature character for stranding, instead of plain juxtaposition. It also has a list notation using ⟨⟩, and [] for higher-rank arrays.

For reading

-

Here are some closest equivalents in Dyalog APL for the BQN functions that don't use the same glyphs as APL. Correspondence can be approximate, and is just used as a decorator to mean "reverse some things".

+

Here are some closest equivalents in Dyalog APL for the BQN functions that don't use the same glyphs. Correspondence can be approximate, and is just used as a decorator to mean "reverse some things".

@@ -265,16 +264,34 @@ - + - +
¨ . A ⍤f
-

In BQN is Rank and is Atop. Dyalog's Atop () and Over () were added in version 18.0.

+

Some other BQN modifiers have been proposed as future Dyalog extensions:

+ + + + + + + + + + + + + + + + + +
BQN
Dyalog proposed Under Depth Reverse Compose

For writing

The tables below give approximate implementations of Dyalog primitives for the ones that aren't the same. First- and last-axis pairs are also mostly omitted. BQN just has the first-axis form, and you can get the last-axis form with 1.

The form FG (Power with a function right operand; Power limit) must be implemented with recursion instead of primitives because it performs unbounded iteration. The modifier _while_ {𝔽𝔾𝔽_𝕣_𝔾𝔽𝔾𝕩} provides similar functionality without risk of stack overflow. It's discussed here and called as Fn _while_ Cond arg.

@@ -283,7 +300,7 @@ Glyph Monadic Dyadic * - ! ×´1+↕ -˜((×´))1+↕ + ! ×´1+↕ ((×´)1+)˜ π× •math ~ ¬ ¬∊/⊣ ? •rand.Range0 •rand.Deal @@ -294,10 +311,10 @@ ˘ 01 > - <˘ + <˘ < +` <(0<≡) (¬-˜⊢×·+`»>) - {(∾𝕊¨)(0<≡𝕩)𝕩} + {(∾𝕊¨)(0<≡)𝕩} {𝕩(𝕨/)𝕨0𝕩} ∊/⊣ @@ -310,8 +327,8 @@ •BQN •Fmt {+(𝕨×)´𝕩} - {>𝕨|⌊÷`𝕨«˜<𝕩} - Inverse from hereSolve + {𝕨|>⌊÷`𝕨«˜<𝕩} + Inverse, Solve from here N/A diff --git a/docs/doc/fromJ.html b/docs/doc/fromJ.html index 338a17d5..489240d8 100644 --- a/docs/doc/fromJ.html +++ b/docs/doc/fromJ.html @@ -10,10 +10,10 @@

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

-

The J terms "atom" and "element" are used to mean different things by different authors. In BQN, an atom or rank-0 array is called a "unit", and the values contained in an array—which may or may not be arrays—are called "elements". Each element is contained in a 0-cell, or rank-0 subarray. BQN uses the term "major cell" for what J calls an "item" of an array: a cell with rank one less than that array. BQN shares the terms "list" and "table" for rank-1 and rank-2 arrays with J.

+

The J terms "atom" and "element" are used to mean different things by different authors. In BQN, a rank-0 array or atom is called a "unit", and the values contained in an array—which may or may not be arrays—are called "elements". Each element is contained in a 0-cell, or rank-0 subarray. BQN uses the term "major cell" for what J calls an "item" of an array: a cell with rank one less than that array. BQN shares the terms "list" and "table" for rank-1 and rank-2 arrays with J.

BQN uses "depth" rather than "boxing level". BQN gives atoms depth 0, so that the depth of a BQN array is one higher than the boxing level of the corresponding J array.

Roles

-

In J, the part of speech is an inherent property of a value, while in BQN it is determined by how the value is used in a particular expression, and can be different from the value's type. See context-free grammar.

+

In J, the part of speech is an inherent property of a value, while in BQN it's determined by how the value is used in a particular expression, and can be different from the value's type. See context-free grammar.

@@ -58,7 +58,7 @@ - + @@ -107,7 +107,7 @@
' "' creates characters' for character atoms
=. and =:
-

BQN's explicit functions and modifiers are called "blocks", and have a more sophisticated syntax than J; see the documentation. BQN uses lexical scope, and has no global variables. BQN also has a list notation using ⟨⟩.

+

BQN's explicit functions and modifiers are called blocks, and have a more sophisticated syntax than J. BQN uses lexical scope, and has no global variables. BQN also has a list notation using ⟨⟩, and [] for higher-rank arrays.

For reading

J analogues of BQN primitive functions are given below. They are not always the same; usually this is because BQN has extra functionality relative to J, although in some cases it has less or different functionality.

Functions + - | < > are the same in both languages.

@@ -485,8 +485,8 @@ %. -Inverse from here -Solve +Inverse, +Solve from here $ @@ -516,7 +516,7 @@ ,: - + ; @@ -541,7 +541,7 @@ ! ×´1+↕ --˜((×´))1+↕ +((×´)1+)˜ /: diff --git a/docs/doc/functional.html b/docs/doc/functional.html index d7fbad61..ea4d18ca 100644 --- a/docs/doc/functional.html +++ b/docs/doc/functional.html @@ -6,8 +6,8 @@

Functional programming

BQN boasts of its functional capabilities, including first-class functions. What sort of functional support does it have, and how can a BQN programmer exercise these and out themself as a Schemer at heart?

-

First, let's be clear about what the terms we're using mean. A language has first-class functions when functions (however they are defined) can be used in all the same ways as "ordinary" values like numbers and so on, such as being passed as an argument or placed in a list. Lisp and JavaScript have first-class functions, C has unsafe first-class functions via function pointers, and Java 7 and APL don't have them as functions can't be placed in lists or used as arguments. This doesn't mean every operation is supported on functions: for instance, numbers can be added, compared, and sorted; while functions could perhaps be added to give a train, comparing or sorting them as functions (not representations) isn't computable, and BQN doesn't support any of the three operations when passing functions as arguments.

-

Traditionally, APL has worked around its lack of first-class functions with operators, that is, second-order functions. Arrays in APL are first class while functions are second class and operators are third class, and each class can act on the ones before it. However, the three-tier system has some obvious limitations that we'll discuss, and BQN removes these by making every type first class.

+

First, let's be clear about what the terms we're using mean. A language has first-class functions when functions (however they are defined) can be used in all the same ways as "ordinary" values like numbers and so on, such as being passed as an argument or placed in a list. Lisp and JavaScript have first-class functions, and C has unsafe first-class functions via function pointers. Java 7 and APL don't have them, as functions can't be placed in lists or used as arguments. This doesn't mean every operation is supported on functions: for instance, numbers can be added, compared, and sorted; while functions could perhaps be added to give a train, comparing or sorting them as functions (not representations) isn't computable, and BQN doesn't support any of the three operations when passing functions as arguments.

+

Traditionally, APL has worked around its lack of first-class functions with operators, that is, second-order functions. Arrays in APL are first class while functions are second class and operators are third class, and each class can act on the ones above it. However, the three-tier system has some obvious limitations that we'll discuss, and BQN removes these by making every type first class.

"Functional programming" @@ -59,8 +59,8 @@ -

The term functional programming is more contentious, and has many meanings some of which can be vague. Here I use it for what might be called first-class functional programming, programming that makes significant use of first-class functions; in this usage, Scheme is probably the archetypal functional programming language. However, other definitions are also worth mentioning. APL is often called a functional programming language on the grounds that functions can be assigned and manipulated, and called recursively, all characteristics it shares with Lisp. I prefer the term function-level programming for this usage. A newer usage, which I call pure functional programming, restricts the term "function" to mathematical functions, which have no side effects, so that functional programming is programming with no side effects, often using monads to accumulate effects as part of arguments and results instead. Finally, typed functional programming is closely associated with pure functional programming and refers to languages influenced by type theory such as Haskell, F#, and Idris (the last of which even supports dependently-typed functional programming, but I already said "finally" so we'll stop there). Of these, BQN supports first-class functional and function-level programming, allows but doesn't encourage pure functional programming, and does not support typed functional programming, as it's dynamically and not statically typed.

-

Another topic we are interested in is lexical scoping and closures. Lexical scoping means that the realm in which a variable exists is determined by its containing context (in BQN, the surrounding set of curly braces {}, if any) within the source code. A closure is really an implementation mechanism, but it's often used to refer to a property of lexical scoping that appears when functions defined in a particular block can be accessed after the block finishes execution. For example, they might be returned from a function or assigned to a variable outside of that function's scope. In this case the functions can still access variables in the original scope. I consider this property to be a requirement for a correct lexical scoping implementation, but it's traditionally not a part of APL: implementation might not have lexical scoping (for example, J and I believe A+ use static scoping where functions can't access variables in containing scopes) or might cut off the scope once execution ends, leading to value errors that one wouldn't predict from the rules of lexical scoping.

+

The term functional programming is more contentious, and has many meanings some of which can be vague. Here I use it for what might be called first-class functional programming, programming that makes significant use of first-class functions; in this usage, Scheme is probably the archetypal functional programming language. However, other definitions are also worth mentioning. APL is often called a functional programming language on the grounds that functions can be assigned and manipulated, and called recursively, all characteristics it shares with Lisp. I prefer the term function-level programming for this usage. A newer usage, which I call pure functional programming, restricts the term "function" to mathematical functions, which have no side effects, so that functional programming is programming with no side effects, often using monads to accumulate effects as part of arguments and results instead. Finally, typed functional programming is closely associated with pure functional programming and refers to languages influenced by type theory such as Haskell, F#, and Idris (the last of which even supports dependently-typed functional programming, but I already said "finally" so we'll stop there). Of these, BQN supports first-class functional and function-level programming, allows but doesn't encourage pure functional programming, and doesn't support typed functional programming, as it's dynamically and not statically typed.

+

Another topic we're interested in is lexical scoping and closures. Lexical scoping means that the realm in which a variable exists is determined by its containing context (in BQN, the surrounding set of curly braces {}, if any) within the source code. A closure is really an implementation mechanism, but it's often used to refer to a property of lexical scoping that appears when functions defined in a particular block can be accessed after the block finishes execution. For example, they might be returned from a function or assigned to a variable outside of that function's scope. In this case the functions can still access variables in the original scope. I consider this property to be a requirement for a correct lexical scoping implementation, but it's traditionally not a part of APL: implementation might not have lexical scoping (for example, J and K use static scoping where functions can't access variables in containing scopes) or might cut off the scope once execution ends, leading to value errors that one wouldn't predict from the rules of lexical scoping.

Functions in APL

This seems like a good place for a brief and entirely optional discussion of how APL handles functions and why it does it this way. As mentioned above, APL's functions are second class rather than first class. But the barriers to making functions first-class objects have been entirely syntactic and conceptual, not technical. In fact, the J language has for a long time had a bug that allows an array containing a function to be created: by selecting from the array, the function itself can even be passed through tacit functions as an argument!

The primary reason why APL doesn't allow functions to be passed as arguments is probably syntax: in particular, there's no way to say that a function should be used as the left argument to another function, as an expression like F G x with functions F and G and an array x will simply be evaluated as two monadic function applications. However, there's no syntactic rule that prevents a function from returning a function, and Dyalog APL for example allows this (so '+' returns the function +). Dyalog's OR is another interesting phenomenon in this context: it creates an array from a function or operator, which can then be used as an element or argument like any array. The mechanism is essentially the same as BQN's first class functions, and in fact ORs even share a form of BQN's syntactic type erasure, as a OR of a function passed as an operand magically becomes a function again. But outside of this property, it's cumbersome and slow to convert functions to and from ORs, so they don't work very well as a first-class function mechanism.

@@ -86,7 +86,7 @@ ExpLin 5 9.591409142295225
-

A tricker but more compact method is to use the 1-modifier {𝔽}, as the input to a modifier can have a subject or function role but its output always has a function role.

+

A tricker but more compact method is to use the 1-modifier {𝔽}, as a modifier's operand can have a subject or function role but its output always has a function role.

↗️
    (Lin exp){𝔽} 5
 9.591409142295225
 
@@ -119,7 +119,7 @@ ↗️
    , 2, ⊢-⋆ {𝕎𝕩}¨ 9
 ⟨ 3 ⟨ 2 9 ⟩ ¯8094.083927575384 ⟩
 
-

The 2-modifier Choose () relies on arrays of functions to… function. It's very closely related to Pick , and in fact when the left operand and the elements of the right operand are all data there's no real difference: Choose returns the constant function 𝕗𝕘.

+

The 2-modifier Choose () relies on arrays of functions to… function. It's very closely related to Pick , and in fact when the left operand and the elements of the right operand are all data there's no real difference: Choose results in the constant function 𝕗𝕘.

↗️
    2"abcdef" "arg"
 'c'
 
diff --git a/docs/doc/glossary.html b/docs/doc/glossary.html index 13581749..3588a8d8 100644 --- a/docs/doc/glossary.html +++ b/docs/doc/glossary.html @@ -37,34 +37,23 @@
  • Real number (more accurately, approximate doubly-extended real number): A number with no complex part.
  • Complex number: A real number plus i (one of the square roots of -1) times another real number.
  • -

    Roles

    - -

    The possible roles are:

    -

    Arrays

    -

    Namespaces

    -

    Tokens

    -

    Parsing

    +

    Grammar

    +

    Roles

    + +

    The possible roles are:

    +

    Assignment and scoping