From 0c716e4c6b7c2c44bbfd02b6503cae66af7b7480 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Fri, 28 Jan 2022 16:34:41 -0500 Subject: Separate syntax highlighting category for header/body characters ;:? --- docs/doc/block.html | 58 +++++++++++----------- docs/doc/control.html | 56 +++++++++++----------- docs/doc/couple.html | 2 +- docs/doc/embed.html | 12 ++--- docs/doc/expression.html | 6 +-- docs/doc/fromDyalog.html | 2 +- docs/doc/fromJ.html | 122 +++++++++++++++++++++++------------------------ docs/doc/glossary.html | 4 +- docs/doc/oop.html | 4 +- docs/doc/pair.html | 2 +- docs/doc/primitive.html | 2 +- docs/doc/rebqn.html | 2 +- docs/doc/syntax.html | 10 ++-- docs/doc/undo.html | 4 +- 14 files changed, 143 insertions(+), 143 deletions(-) (limited to 'docs/doc') diff --git a/docs/doc/block.html b/docs/doc/block.html index 64643e7d..40de8ab2 100644 --- a/docs/doc/block.html +++ b/docs/doc/block.html @@ -135,26 +135,26 @@

Because 𝕣 only ever refers to a 1-modifier or 2-modifer, it can never make sense to refer to it as a function, and the uppercase letter ℝ is not recognized by BQN. In order to allow 𝕣 to be spelled as a 1-modifier _𝕣 or 2-modifier _𝕣_, it is treated as an ordinary identifier character, so it must be separated from letters or numbers by spaces.

Block headers

-

As a program becomes larger, it often becomes necessary to name inputs to blocks rather than just using special names. It can also become difficult to identify what kind of block is being defined, as it requires scanning through the block for special names. A block header, which is separated from the body of a block by a colon :, specifies the kind of block and can declare names for the block and its inputs.

-
Fact ← { F n:
+

As a program becomes larger, it often becomes necessary to name inputs to blocks rather than just using special names. It can also become difficult to identify what kind of block is being defined, as it requires scanning through the block for special names. A block header, which is separated from the body of a block by a colon :, specifies the kind of block and can declare names for the block and its inputs.

+
Fact ← { F n:
   n Γ— (0⊸<)β—Ά1β€ΏF n-1
 }
 

Its syntax mirrors an application of the block. As suggested by the positioning, the names given in a header apply only inside the block: for example F above is only defined inside the {} braces while Fact could be used either outside or inside. Some other possibilites are given below.

# A dyadic function that refers to itself as Func
-{ l Func r:
+{ l Func r:
   …
 
 # A deferred 1-modifier with a list argument
-{ Fn _apply ⟨a,b⟩:
+{ Fn _apply ⟨a,b⟩:
   …
 
 # A monadic function with no names given
-{ π•Šπ•©:
+{ π•Šπ•©:
   …
 
 # An immediate or deferred 2-modifier
-{ F _op_ val:
+{ F _op_ val:
   …
 

In all cases special names still work just like in a headerless function. In this respect the effect of the header is the same as a series of assignments at the beginning of a function, such as the following translation of the second header above:

@@ -167,31 +167,31 @@

Unlike these assignments, the header also constrains what inputs the block can take: a monadic 1-modifier like the one above can't take a right operand or left argument, and consequently its body can't contain 𝔾 or 𝕨. Calling it with a left argument, or a right argument that isn't a two-element list, will result in an error.

Destructuring

Arguments, but not operands, allow destructuring like assignment does. While assignment only tolerates lists of variables, header destructuring also allows constants. The argument must match the given structure, including the constants where they appear, or an error results.

-↗️
    Destruct ← { π•Š aβ€Ώ1β€ΏβŸ¨b,2⟩: a≍b }
+↗️
    Destruct ← { π•Š aβ€Ώ1β€ΏβŸ¨b,2⟩: a≍b }
     Destruct       5β€Ώ1β€ΏβŸ¨7,2⟩
 ⟨ 5 7 ⟩
 

Special names in headers

-

Any element of a function or modifier header can be left nameless by using the corresponding special name in that position, instead of an identifier. For example, the header 𝕨 𝔽_𝕣_𝔾 𝕩: incorporates as much vagueness as possible. It indicates a deferred 2-modifier, but provides no other information.

+

Any element of a function or modifier header can be left nameless by using the corresponding special name in that position, instead of an identifier. For example, the header 𝕨 𝔽_𝕣_𝔾 𝕩: incorporates as much vagueness as possible. It indicates a deferred 2-modifier, but provides no other information.

The name 𝕨 in this context can refer to either a left argument or no left argument, allowing a header with arguments to be used even for an ambiguous function. Recall that 𝕨 is the only token other than Β· that can have no value. If an identifier or list is given as the left argument, then the function must be called with a left argument.

Short headers

-

A header does not need to include all inputs, as shown by the F _op_ val: header above. The simplest case, when no inputs are given, is called a label. While it doesn't restrict the inputs, a label specifies the type of the block and gives an internal name that can be used to refer to it.

-
{ b:   # Block
-{ π•Š:   # Function
-{ _𝕣:  # 1-Modifier
-{ _𝕣_: # 2-Modifier
+

A header does not need to include all inputs, as shown by the F _op_ val: header above. The simplest case, when no inputs are given, is called a label. While it doesn't restrict the inputs, a label specifies the type of the block and gives an internal name that can be used to refer to it.

+
{ b:   # Block
+{ π•Š:   # Function
+{ _𝕣:  # 1-Modifier
+{ _𝕣_: # 2-Modifier
 

For immediate blocks, this is the only type of header possible, and it must use an identifier as there is no applicable special name. However, the name can't be used: it doesn't make sense to refer to a value while it is still being computed!

Multiple bodies

-

Blocks that define functions and deferred modifiers can include more than one body, separated by semicolons ;. The body used for a particular evaluation is chosen based on the arguments the the block. One special case applies when there are exactly two bodies either without headers or with labels only: in this case, the first applies when there is one argument and the second when there are two.

-↗️
    Ambiv ← { ⟨1,π•©βŸ© ; ⟨2,𝕨,π•©βŸ© }
+

Blocks that define functions and deferred modifiers can include more than one body, separated by semicolons ;. The body used for a particular evaluation is chosen based on the arguments the the block. One special case applies when there are exactly two bodies either without headers or with labels only: in this case, the first applies when there is one argument and the second when there are two.

+↗️
    Ambiv ← { ⟨1,π•©βŸ© ; ⟨2,𝕨,π•©βŸ© }
     Ambiv 'a'
 ⟨ 1 'a' ⟩
     'a' Ambiv 'b'
 ⟨ 2 'a' 'b' ⟩
 

Bodies before the last two must have headers that include arguments. When a block that includes this type of header is called, its headers are checked in order for compatibility with the arguments. The first body with a compatible header is used.

-↗️
    CaseAdd ← { 2π•Š3:0β€Ώ5 ; 2π•Šπ•©:⟨1,2+π•©βŸ© ; π•Šπ•©:2‿𝕩 }
+↗️
    CaseAdd ← { 2π•Š3:0β€Ώ5 ; 2π•Šπ•©:⟨1,2+π•©βŸ© ; π•Šπ•©:2‿𝕩 }
     2 CaseAdd 3
 ⟨ 0 5 ⟩
     2 CaseAdd 4
@@ -206,16 +206,16 @@
 

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.

Test ← {
-  "abc": "string" ;
-  ⟨2,b⟩: βŒ½π•©       ;
-  5:     "number" ;
-  𝕩:     "default"
+  "abc": "string" ;
+  ⟨2,b⟩: βŒ½π•©       ;
+  5:     "number" ;
+  𝕩:     "default"
 }
 

These case-style headers function exactly the same as if they were preceded by π•Š, and can be mixed with other kinds of headers.

Predicates

-

Destructuring with a header is quite limited, only allowing matching structure and data with exact equality. A predicate, written with ?, allows you to test an arbitrary property before evaluating the rest of the body, and also serves as a limited kind of control flow. It can be thought of as an extension to a header, so that for example the following function requires the argument to have two elements and for the first to be less than the second before using the first body. Otherwise it moves to the next body, which is unconditional.

-↗️
    CheckPair ← { π•ŠβŸ¨a,b⟩: a<b? "ok" ; "not ok" }
+

Destructuring with a header is quite limited, only allowing matching structure and data with exact equality. A predicate, written with ?, allows you to test an arbitrary property before evaluating the rest of the body, and also serves as a limited kind of control flow. It can be thought of as an extension to a header, so that for example the following function requires the argument to have two elements and for the first to be less than the second before using the first body. Otherwise it moves to the next body, which is unconditional.

+↗️
    CheckPair ← { π•ŠβŸ¨a,b⟩: a<b? "ok" ; "not ok" }
 
     CheckPair ⟨3,8⟩    # Fails destructuring
 "ok"
@@ -224,12 +224,12 @@
     CheckPair ⟨3,¯1⟩   # Not ascending
 "not ok"
 
-

The body where the predicate appears doesn't need to start with a header, and there can be other statements before it. In fact, ? functions just like a separator (like β‹„ or ,) with a side effect.

-↗️
    { rβ†βŒ½π•© β‹„ 't'=βŠ‘r ? r ; 𝕩 }Β¨ "test"β€Ώ"this"
+

The body where the predicate appears doesn't need to start with a header, and there can be other statements before it. In fact, ? functions just like a separator (like β‹„ or ,) with a side effect.

+↗️
    { rβ†βŒ½π•© β‹„ 't'=βŠ‘r ? r ; 𝕩 }Β¨ "test"β€Ώ"this"
 ⟨ "tset" "this" ⟩
 
-

So r is the reversed argument, and if its first character (the last one in 𝕩) is 't' then it returns r, and otherwise we abandon that line of reasoning and return 𝕩. This sounds a lot like an if statement. And { a<b ? a ; b }, which computes a⌊b the hard way, shows how the syntax can be similar to a ternary operator. This is an immediate block with multiple bodies, something that makes sense with predicates but not headers. But ?; offers more possibilities. It can support any number of options, with multiple tests for each oneβ€”the structure below is "if _ and _ then _; else if _ then _; else _".

-↗️
    Thing ← { 𝕩β‰₯3? 𝕩≀8? 2|𝕩 ; 𝕩=0? @ ; ∞ }
+

So r is the reversed argument, and if its first character (the last one in 𝕩) is 't' then it returns r, and otherwise we abandon that line of reasoning and return 𝕩. This sounds a lot like an if statement. And { a<b ? a ; b }, which computes a⌊b the hard way, shows how the syntax can be similar to a ternary operator. This is an immediate block with multiple bodies, something that makes sense with predicates but not headers. But ?; offers more possibilities. It can support any number of options, with multiple tests for each oneβ€”the structure below is "if _ and _ then _; else if _ then _; else _".

+↗️
    Thing ← { 𝕩β‰₯3? 𝕩≀8? 2|𝕩 ; 𝕩=0? @ ; ∞ }
 
     (⊒ ≍ ThingΒ¨) ↕10  # Table of arguments and results
 β”Œβ”€                     
@@ -237,8 +237,8 @@
   @ ∞ ∞ 1 0 1 0 1 0 ∞  
                       β”˜
 
-

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"
+

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

+

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/control.html b/docs/doc/control.html index 5abfa166..b4426630 100644 --- a/docs/doc/control.html +++ b/docs/doc/control.html @@ -10,26 +10,26 @@

The surfeit of ways to write control structures could be a bit of an issue for reading BQN. My hope is that the community can eventually settle on a smaller set of standard forms to recommend so that you won't have to recognize all the variants given here. On the other hand, the cost of using specialized control structures is lower in a large project without too many contributors. In this case BQN's flexibility allows developers to adapt to the project's particular demands (for example, some programs use switch/case statements heavily but most do not).

The useful control structures introduced here are collected as shortened definitions below. While uses the slightly more complicated implementation that avoids stack overflow, and DoWhile and For are written in terms of it in order to share this property. The more direct versions with linear stack use appear in the main text.

If      ← {π•βŸπ•Ž@}Β΄                 # Also Repeat
-IfElse  ← {cβ€ΏTβ€ΏF: cβ—ΆFβ€ΏT@}
+IfElse  ← {cβ€ΏTβ€ΏF: cβ—ΆFβ€ΏT@}
 While   ← {𝕩{π”½βŸπ”Ύβˆ˜π”½_𝕣_π”Ύβˆ˜π”½βŸπ”Ύπ•©}𝕨@}Β΄  # While 1β€Ώ{... to run forever
 DoWhile ← {𝕏@ β‹„ While 𝕨‿𝕩}Β΄
-For     ← {Iβ€ΏCβ€ΏPβ€ΏA: I@ β‹„ While⟨C,P∘A⟩}
+For     ← {Iβ€ΏCβ€ΏPβ€ΏA: I@ β‹„ While⟨C,P∘A⟩}
 
 # Switch/case statements have many variations; these are a few
 Match   ← {𝕏𝕨}Β΄
 Select  ← {(βŠ‘π•©)β—Ά(1↓𝕩)@}
 Switch  ← {cβ†βŠ‘π•© β‹„ mβ€Ώa←<Λ˜β‰βˆ˜β€Ώ2β₯Š1↓𝕩 β‹„ (βŠ‘a⊐C)β—Άm@}
-Test    ← {fn←{Cβ€ΏAπ•Še:Cβ—ΆAβ€ΏE}´𝕩⋄Fn@}
+Test    ← {fn←{Cβ€ΏAπ•Še:Cβ—ΆAβ€ΏE}´𝕩⋄Fn@}
 

Blocks and functions

Control structures are generally defined to work with blocks of code, which they might skip, or execute one or more times. This might sound like a BQN immediate block, which also consists of a sequence of code to execute, but immediate blocks are always executed as soon as they are encountered and can't be manipulated the way that blocks in imperative languages can. They're intended to be used with lexical scoping as a tool for encapsulation. Instead, the main tool we will use to get control structures is the block function.

Using functions as blocks is a little outside their intended purpose, and the fact that they have to be passed an argument and are expected to use it will be a minor annoyance. The following conventions signal a function that ignores its argument and is called purely for the side effects:

  • Pass @ to a function that ignores its argument. It's a nice signal that nothing is happening and is easy to type.
  • -
  • A headerless function that doesn't use an argument will be interpreted as an immediate block by default. Start it with the line 𝕀 to avoid this (it's an instruction to navel gaze: the function contemplates its self, but does nothing about it). Other options like π•Š:, F:, or 𝕩 also work, but are more visually distracting.
  • +
  • A headerless function that doesn't use an argument will be interpreted as an immediate block by default. Start it with the line 𝕀 to avoid this (it's an instruction to navel gaze: the function contemplates its self, but does nothing about it). Other options like π•Š:, F:, or 𝕩 also work, but are more visually distracting.

Even with these workarounds, BQN's "niladic" function syntax is quite lightweight, comparing favorably to a low-boilerplate language like Javascript.

-
fn = ()=>{m+=1;n*=2}; fn()
+
fn = ()=>{m+=1;n*=2}; fn()
 Fn ← {𝕀⋄  m+↩1,n×↩2}, Fn @
 

Control structures are called "statements" below to match common usage, but they are actually expressions, and return a value that might be used later.

@@ -49,7 +49,7 @@

The result of any of these if statements is the result of the action if it's performed, and otherwise it's whatever argument was passed to the statement, which is @ or 10 here.

BQN's syntax for a pure if statement isn't so good, but predicates handle if-else statements nicely. So in most cases you'd forego the definitions above in favor of an if-else with nothing in the else branch:

-
{ a<10 ? a+↩10 ; @ }
+
{ a<10 ? a+↩10 ; @ }
 

Repeat

There's no reason the condition in an if statement from the previous section has to be boolean: it could be any natural number, causing the action to be repeated that many times. If the action is never performed, the result is the statement's argument, and otherwise it's the result of the last time the action was performed.

@@ -57,14 +57,14 @@

If-Else

In most cases, the easy way to write an if-else statement is with predicates:

{
-  threshold < 6 ?
-  a ↩ Small threshold ;  # If predicate was true
+  threshold < 6 ?
+  a ↩ Small threshold ;  # If predicate was true
   b ↩ 1 Large threshold  # If it wasn't
 }
 

We might also think of an if-else statement as a kind of switch-case statement, where the two cases are true (1) and false (0). As a result, we can implement it either with Choose (β—Ά) or with case headers of 1 and 0.

When using Choose, note that the natural ordering places the false case before the true one to match list index ordering. To get the typical if-else order, the condition should be negated or the statements reversed. Here's a function to get an if-else statement by swapping the conditions, and two ways its application might be written.

-
IfElse ← {condβ€ΏTrueβ€ΏFalse: condβ—ΆFalseβ€ΏTrue @}
+
IfElse ← {condβ€ΏTrueβ€ΏFalse: condβ—ΆFalseβ€ΏTrue @}
 
 IfElse βŸ¨π•©<midβŠ‘π•¨
   {𝕀⋄ hi↩mid}
@@ -79,22 +79,22 @@
 

Case headers have similar syntax, but the two cases are labelled explicitly. In this form, the two actions are combined in a single function, which could be assigned to call it on various conditions.

{𝕏𝕨}Β΄ (𝕩<midβŠ‘π•¨)β€Ώ{
-  1: hi↩mid
-;
-  0: lo↩mid
+  1: hi↩mid
+;
+  0: lo↩mid
 }
 

The result of an if-else statement is just the result of whichever branch was used; chained if-else and switch-case statements will work the same way.

Chained If-Else

One pattern in imperative languages is to check one condition and apply an action if it succeeds, but check a different condition if it fails, in sequence until some condition succeeds or every one has been checked. Languages might make this pattern easier by making if-else right associative, so that the programmer can write an if statement followed by a sequence of else if "statements", or might just provide a unified elif keyword that works similarly. BQN's predicates work really well for this structure:

{
-  a<b ? a+↩1 ;
-  a<c ? c-↩1 ;
+  a<b ? a+↩1 ;
+  a<c ? c-↩1 ;
         a-↩2
 }
 

For a function-based approach, it's possible to nest IfElse expressions, but it's also possible to write a control structure that chains them all at one level. For this statement the input will be a sequence of ⟨Test,Action⟩ pairs, followed by a final action to perform if no test succeeds. The first test is always performed; other tests should be wrapped in blocks because otherwise they'll be executed even if an earlier test succeeded.

-
Test ← {fn←{Condβ€ΏAct π•Š else: Condβ—ΆElseβ€ΏAct}´𝕩 β‹„ Fn@}
+
Test ← {fn←{Condβ€ΏAct π•Š else: Condβ—ΆElseβ€ΏAct}´𝕩 β‹„ Fn@}
 
 Test ⟨
   (  a<b)β€Ώ{𝕀⋄a+↩1}
@@ -107,11 +107,11 @@
 
Match ← {𝕏𝕨}Β΄
 
 Match valueβ€Ώ{
-  0β€Ώb: n-↩b
-;
-  aβ€Ώb: n+↩a-b
-;
-  𝕩: nβˆΎβ†©π•©
+  0β€Ώb: n-↩b
+;
+  aβ€Ώb: n+↩a-b
+;
+  𝕩: nβˆΎβ†©π•©
 }
 

A simplified version of a switch-case statement is possible if the cases are natural numbers 0, 1, and so on. The Choose (β—Ά) modifier does just what we want. The Select statement below generalizes IfElse, except that it doesn't rearrange the cases relative to Choose while IfElse swaps them.

@@ -144,7 +144,7 @@ } arg

To convert this to a control structure format, we want to take an action A, and produce a function that runs A, then runs itself. Finally we want to call that function on some argument, say @. The argument is a single function, so to call Forever, we need to convert that function to a subject role.

-
Forever ← {π•Ša:{π•ŠA𝕩}@}
+
Forever ← {π•Ša:{π•ŠA𝕩}@}
 
 Forever 1βŠ‘@β€Ώ{𝕀
   # Stuff to do forever
@@ -184,13 +184,13 @@
 FnΒ¨ βŒ½β†•n    # for (𝕩=n; --𝕩; )
 

Very well… a for loop is just a while loop with some extra pre- and post-actions.

-
For ← {Preβ€ΏCondβ€ΏPostβ€ΏAct: Pre@ β‹„ {π•Šβˆ˜Post∘Act⍟Cond 𝕩}@}
+
For ← {Preβ€ΏCondβ€ΏPostβ€ΏAct: Pre@ β‹„ {π•Šβˆ˜Post∘Act⍟Cond 𝕩}@}
 
 For (c←27⊣n←0)β€Ώ{𝕀⋄1<c}β€Ώ{𝕀⋄n+↩1}β€Ώ{𝕀
   {𝕏𝕨}Β΄ (2|c)β€Ώ{
-    0: c÷↩2
-  ;
-    1: c↩1+3Γ—c
+    0: c÷↩2
+  ;
+    1: c↩1+3Γ—c
   }
 }
 
@@ -199,9 +199,9 @@
c←27 β‹„ n←0
 While ⟨{𝕀⋄1<c}, {𝕀⋄n+↩1}{π”Ύβˆ˜π”½}{𝕀
   Match (2|c)β€Ώ{
-    0: c÷↩2
-  ;
-    1: c↩1+3Γ—c
+    0: c÷↩2
+  ;
+    1: c↩1+3Γ—c
   }
 }⟩
 
diff --git a/docs/doc/couple.html b/docs/doc/couple.html index 5f9b8dd5..1f71bb19 100644 --- a/docs/doc/couple.html +++ b/docs/doc/couple.html @@ -78,5 +78,5 @@

A note on the topic of Solo and Couple applied to units. As always, one axis will be added, so that the result is a list (strangely, J's laminate differs from Couple in this one case, as it will add an axis to get a shape 2β€Ώ1 result). For Solo, this is interchangeable with Deshape (β₯Š), and either primitive might be chosen for stylistic reasons. For Couple, it is equivalent to Join-to (∾), but this is an irregular form of Join-to because it is the only case where Join-to adds an axis to both arguments instead of just one. Couple should be preferred in this case.

The function Pair (β‹ˆ) can be written ≍○<, while ≍ in either valence is >βˆ˜β‹ˆ. As an interesting consequence, ≍ ←→ >βˆ˜β‰β—‹<, and β‹ˆ ←→ >βˆ˜β‹ˆβ—‹<. These two identities have the same form because adding β—‹< commutes with adding >∘.

Definitions

-

As discussed above, ≍ is equivalent to >{βŸ¨π•©βŸ©;βŸ¨π•¨,π•©βŸ©}. To complete the picture we should describe Merge fully. Merge is defined on an array argument 𝕩 such that there's some shape s satisfying ∧´β₯Š(s≑≒)¨𝕩. If 𝕩 is empty then any shape satisfies this expression; s should be chosen based on known type information for 𝕩 or otherwise assumed to be ⟨⟩. If s is empty then 𝕩 is allowed to contain atoms as well as unit arrays, and these will be implicitly promoted to arrays by the βŠ‘ indexing used later. We construct the result by combining the outer and inner axes of the argument with Table; since the outer axes come first they must correspond to the left argument and the inner axes must correspond to the right argument. 𝕩 is a natural choice of left argument, and because no concrete array can be used, the right argument will be ↕s, the array of indices into any element of 𝕩. To get the appropriate element corresponding to a particular choice of index and element of 𝕩 we should select using that index. The result of Merge is π•©βŠ‘ΛœβŒœβ†•s.

+

As discussed above, ≍ is equivalent to >{βŸ¨π•©βŸ©;βŸ¨π•¨,π•©βŸ©}. To complete the picture we should describe Merge fully. Merge is defined on an array argument 𝕩 such that there's some shape s satisfying ∧´β₯Š(s≑≒)¨𝕩. If 𝕩 is empty then any shape satisfies this expression; s should be chosen based on known type information for 𝕩 or otherwise assumed to be ⟨⟩. If s is empty then 𝕩 is allowed to contain atoms as well as unit arrays, and these will be implicitly promoted to arrays by the βŠ‘ indexing used later. We construct the result by combining the outer and inner axes of the argument with Table; since the outer axes come first they must correspond to the left argument and the inner axes must correspond to the right argument. 𝕩 is a natural choice of left argument, and because no concrete array can be used, the right argument will be ↕s, the array of indices into any element of 𝕩. To get the appropriate element corresponding to a particular choice of index and element of 𝕩 we should select using that index. The result of Merge is π•©βŠ‘ΛœβŒœβ†•s.

Given this definition we can also describe Rank (βŽ‰) in terms of Each (Β¨) and the simpler monadic function Enclose-Rank <βŽ‰k. We assume effective ranks j for 𝕨 (if present) and k for 𝕩 have been computed. Then the correspondence is 𝕨FβŽ‰k𝕩 ←→ >(<βŽ‰j𝕨)FΒ¨(<βŽ‰k𝕩).

diff --git a/docs/doc/embed.html b/docs/doc/embed.html index bdd03679..b02342cc 100644 --- a/docs/doc/embed.html +++ b/docs/doc/embed.html @@ -9,16 +9,16 @@

There is only one mechanism to interface between the host language and BQN: the function bqn evaluates a string containing a BQN program and returns the result. Doesn't sound like much, especially considering these programs can't share any state such as global variables (BQN doesn't have those). But taking first-class functions and closures into account, it's all you could ever need!

Passing closures

Probably you can figure out the easy things like calling bqn("Γ—Β΄1+↕6") to compute six factorial. But how do you get JS and BQN to talk to each other, for example to compute the factorial of a number n? Constructing a source string with bqn("Γ—Β΄1+↕"+n) isn't the best wayβ€”in fact I would recommend you never use this strategy.

-

Instead, return a function from BQN and call it: bqn("{Γ—Β΄1+↕𝕩}")(n). This strategy also has the advantage that you can store the function, so that it will only be compiled once. Define let fact = bqn("{Γ—Β΄1+↕𝕩}"); at the top of your program and use it as a function elsewhere.

+

Instead, return a function from BQN and call it: bqn("{Γ—Β΄1+↕𝕩}")(n). This strategy also has the advantage that you can store the function, so that it will only be compiled once. Define let fact = bqn("{Γ—Β΄1+↕𝕩}"); at the top of your program and use it as a function elsewhere.

BQN can also call JS functions, to use functionality that isn't native to BQN or interact with a program written in JS. For example, bqn("{𝕏'a'+↕26}")(alert) calls the argument alert from within BQN. The displayed output isn't quite right here, because a BQN string is stored as a JS array, not a string. See the next section for more information.

Cool, but none of these examples really use closures, just self-contained functions. Closures are functions that use outside state, which is maintained over the course of the program. Here's an example program that defines i and then returns a function that manipulates i and returns its new value.

let push = bqn(`
     i←4β₯Š0
     {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 4β₯Šn.

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.

@@ -28,7 +28,7 @@ RotY ← {aβ†©π•©βŒ½a} Flip ← {𝕀⋄a↩⍉a} RotXβ€ΏRotYβ€ΏFlip -`); +`);

When defining closures for their side effects like this, make sure they are actually functions! For example, since flip ignores its argument (you can call it with flip(), because a right argument of undefined isn't valid but will just be ignored), it needs an extra 𝕀 in the definition to be a function instead of an immediate block.

You can also use an array to pass multiple functions or other values from JS into BQN all at once. However, a JS array can't be used directly in BQN because its shape isn't known. The function list() converts a JS array into a BQN list by using its length for the shape; the next section has a few more details.

diff --git a/docs/doc/expression.html b/docs/doc/expression.html index 8676b3d9..6b7f588c 100644 --- a/docs/doc/expression.html +++ b/docs/doc/expression.html @@ -22,7 +22,7 @@ -w? +w? F x Subject @@ -30,7 +30,7 @@ RtL, looser -F? +F? G H Function @@ -56,7 +56,7 @@

The four roles (subject, function, two kinds of modifier) describe expressions, not values. When an expression is evaluated, the value's type doesn't have to correspond to its role, and can even change from one evaluation to another. An expression's role is determined entirely by its source code, so it's fixed.

-

In the table, ? marks an optional left argument. If there isn't a value in that position, or it's Nothing (Β·), the middle function will be called with only one argument.

+

In the table, ? marks an optional left argument. If there isn't a value in that position, or it's Nothing (Β·), the middle function will be called with only one argument.

If you're comfortable reading BNF and want to understand things in more detail than described below, you might check the grammar specification as well.

Syntactic role

This issue is approached from a different angle in Context free grammar.

diff --git a/docs/doc/fromDyalog.html b/docs/doc/fromDyalog.html index ac3d4974..1f256416 100644 --- a/docs/doc/fromDyalog.html +++ b/docs/doc/fromDyalog.html @@ -286,7 +286,7 @@ ! Γ—Β΄1+↕ -˜(+Γ·β—‹(Γ—Β΄)⊒)1+β†•βˆ˜βŠ£ β—‹ Ο€βŠΈΓ— β€’math ~ Β¬ ¬∘∊/⊣ - ? β€’rand.Rangeβš‡0 β€’rand.Deal + ? β€’rand.Rangeβš‡0 β€’rand.Deal ⍲ ¬∘∧ ⍱ ¬∘∨ ⍴ β‰’ β₯Š diff --git a/docs/doc/fromJ.html b/docs/doc/fromJ.html index a0dfde9e..524ec8d3 100644 --- a/docs/doc/fromJ.html +++ b/docs/doc/fromJ.html @@ -61,18 +61,18 @@ ' creates characters -=. and =: +=. and =: ← and ↩ ← to define; ↩ to modify -3 :… or {{…}} +3 :… or {{…}} {…} -: -; +: +; To separate function cases @@ -96,7 +96,7 @@ -[: +[: Β· Cap @@ -133,11 +133,11 @@ * % ^ -%: +%: <. >. -<: ->: +<: +>: [ ] @@ -156,10 +156,10 @@ J -,: -,&:< +,: +,&:< |. -|: +|: @@ -181,15 +181,15 @@ Monad -/:~ -\:~ +/:~ +\:~ -. #@$ # L. $ , -; +; Dyad @@ -197,9 +197,9 @@ +. +-. = -~: --: --.@-: +~: +-: +-.@-: $ , @@ -256,22 +256,22 @@ Monad -/: -/: +/: +/: {. -0{::, +0{::, i.~~. … -~: +~: ~. </.i.@# Dyad I. -I.&:- +I.&:- { -{:: +{:: i. … e. @@ -300,12 +300,12 @@ J "_ ~ -@: -&: -&.: -: +@: +&: +&.: +: @. -:: +:: @@ -336,9 +336,9 @@ /\ "_1 " -L: -^: -^:_1 +L: +^: +^:_1 @@ -357,13 +357,13 @@ @+↕256 -a: +a: <↕0

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

-

Some other primitives are essentially the same in J and BQN, but with different spellings (but transpose behaves differently; J's dyadic |: is more like ⍉⁼):

+

Some other primitives are essentially the same in J and BQN, but with different spellings (but transpose behaves differently; J's dyadic |: is more like ⍉⁼):

@@ -372,13 +372,13 @@ - + - + @@ -403,14 +403,14 @@ - - - - + + + + - - - + + + @@ -444,12 +444,12 @@ - + - + @@ -459,7 +459,7 @@ - + @@ -469,7 +469,7 @@ - + @@ -479,7 +479,7 @@ - + @@ -499,7 +499,7 @@ - + @@ -514,12 +514,12 @@ - + - + @@ -544,12 +544,12 @@ - + - + @@ -564,12 +564,12 @@ - + - + @@ -579,7 +579,7 @@ - + @@ -589,12 +589,12 @@ - + - + @@ -614,7 +614,7 @@ - + @@ -673,11 +673,11 @@ - - + + - + diff --git a/docs/doc/glossary.html b/docs/doc/glossary.html index 1220fa6c..75048532 100644 --- a/docs/doc/glossary.html +++ b/docs/doc/glossary.html @@ -139,8 +139,8 @@
  • Block modifier: A block defining a 1- or 2-modifier.
  • Immediate modifier: A modifier that's evaluated as soon as it receives its operands.
  • Deferred modifier: The opposite of an immediate modifier, one that's only evaluated when called with operands and arguments.
  • -
  • Header: A preface to a body in a block function or modifier indicating possible inputs, which is followed by a colon :.
  • +
  • Header: A preface to a body in a block function or modifier indicating possible inputs, which is followed by a colon :.
  • Label: A header consisting of a single name.
  • -
  • Body: One sequence of statements in a block. Bodies, possibly preceded by headers, are separated by semicolons ;.
  • +
  • Body: One sequence of statements in a block. Bodies, possibly preceded by headers, are separated by semicolons ;.
  • Tacit: Code that defines functions or modifiers without using blocks.
  • diff --git a/docs/doc/oop.html b/docs/doc/oop.html index af0daaa2..66df070f 100644 --- a/docs/doc/oop.html +++ b/docs/doc/oop.html @@ -71,7 +71,7 @@ View⇐{𝕀l} - Move⇐{fromβ€Ώto: + Move⇐{fromβ€Ώto:l↩Transfer´⌾(π•©βŠΈβŠ)⍟(≠´𝕩)l}# Move a disk from 𝕨 to 𝕩 @@ -137,7 +137,7 @@ Undo ⇐ t.Move∘⌽∘Pop } -

    This class composes a Tower of Hanoi with an undo stack that stores previous moves. To undo a move from a to b, it moves from b to a, although if you felt really fancy you might define Move⁼ in towerOfHanoi instead with π•ŠβΌπ•©: π•ŠβŒ½π•©.

    +

    This class composes a Tower of Hanoi with an undo stack that stores previous moves. To undo a move from a to b, it moves from b to a, although if you felt really fancy you might define Move⁼ in towerOfHanoi instead with π•ŠβΌπ•©: π•ŠβŒ½π•©.

    It's also possible to copy several variables and only export some of them, with an export statement. For example, if I wasn't going to make another method called Move, I might have written Viewβ€ΏMove ← towerOfHanoi and then View⇐. In fact, depending on your personal style and how complicated your classes are, you might prefer to avoid inline ⇐ exports entirely, and declare all the exports at the top.

    Self-reference

    An object's class is given by π•Š. Remember, a class is an ordinary BQN function! It might be useful for an object to produce another object of the same class (particularly if it's immutable), and an object might also expose a field class⇐𝕀 to test whether an object o belongs to a class c with o.class = c.

    diff --git a/docs/doc/pair.html b/docs/doc/pair.html index a4bf9b75..12165060 100644 --- a/docs/doc/pair.html +++ b/docs/doc/pair.html @@ -62,4 +62,4 @@ ↗️
        4 ↑ "a"β€Ώ5 β‹ˆ "b"β€Ώ7
     ⟨ ⟨ "a" 5 ⟩ ⟨ "b" 7 ⟩ ⟨ " " 0 ⟩ ⟨ " " 0 ⟩ ⟩
     
    -

    This means that β‹ˆ may always behave the same as the obvious implementation {βŸ¨π•©βŸ©;βŸ¨π•¨,π•©βŸ©}. However, ≍○< and even >∘{βŸ¨π•©βŸ©;βŸ¨π•¨,π•©βŸ©}β—‹< compute the result fill as β‹ˆ does and are identical implementations.

    +

    This means that β‹ˆ may always behave the same as the obvious implementation {βŸ¨π•©βŸ©;βŸ¨π•¨,π•©βŸ©}. However, ≍○< and even >∘{βŸ¨π•©βŸ©;βŸ¨π•¨,π•©βŸ©}β—‹< compute the result fill as β‹ˆ does and are identical implementations.

    diff --git a/docs/doc/primitive.html b/docs/doc/primitive.html index 05cb3dbc..2f39dda5 100644 --- a/docs/doc/primitive.html +++ b/docs/doc/primitive.html @@ -468,7 +468,7 @@
    - + diff --git a/docs/doc/rebqn.html b/docs/doc/rebqn.html index 81a00418..932a647f 100644 --- a/docs/doc/rebqn.html +++ b/docs/doc/rebqn.html @@ -75,4 +75,4 @@ ⟨ 0 1 2 0 ⟩

    Above, ^ becomes a 1-modifier, so that it modifies % rather than being called directly on 1β€Ώ2 as a function.

    -

    The glyph can be any character that's not being used by BQN already. Characters like c or ⟩ or : will result in an error, as they'd break BQN syntax. Other than that, the sky's the limit! Or rather, the Unicode consortium is the limit. If they don't recognize your symbol, you're going to have to petition to make it an emoji or something. Oh well.

    +

    The glyph can be any character that's not being used by BQN already. Characters like c or ⟩ or : will result in an error, as they'd break BQN syntax. Other than that, the sky's the limit! Or rather, the Unicode consortium is the limit. If they don't recognize your symbol, you're going to have to petition to make it an emoji or something. Oh well.

    diff --git a/docs/doc/syntax.html b/docs/doc/syntax.html index 6eed337e..460fc460 100644 --- a/docs/doc/syntax.html +++ b/docs/doc/syntax.html @@ -69,15 +69,15 @@ - + - + - + @@ -140,7 +140,7 @@ - + @@ -149,7 +149,7 @@ - + diff --git a/docs/doc/undo.html b/docs/doc/undo.html index f2082a10..3a2ddcba 100644 --- a/docs/doc/undo.html +++ b/docs/doc/undo.html @@ -45,8 +45,8 @@

    Undo headers

    Of course BQN will never be able to invert all the functions you could write (if it could you could earn a lot of bitcoins, among other feats). But it does recognize some header forms that you can use to specify the inverse of a block function. BQN will trust you and won't verify the results your specified inverse gives.

    {
    -  π•Šπ•©:  𝕩÷1+𝕩 ;
    -  π•ŠβΌπ•©: 𝕩÷1-𝕩
    +  π•Šπ•©:  𝕩÷1+𝕩 ;
    +  π•ŠβΌπ•©: 𝕩÷1-𝕩
     }
     

    The above function could also be defined with the automatically invertible 1⊸+⌾÷, but maybe there's a numerical reason to use the definition above. Like a normal header, an undo header reflects the normal use, but it includes ⁼ and possibly ˜ addition to the function and arguments.

    -- cgit v1.2.3
    % ^ ^.%:%: <. >. [ ] |.|:|:
    J ~@:&:&.::@:&:&.:: "L:^:::L:^:::
    =
    <:<: -⟜1 ≀
    >:>: 1⊸+ β‰₯
    ∨
    +:+: 2βŠΈΓ— ¬∨
    ∧
    *:*: Γ—Λœ ¬∧
    ¬∘∊/⊣
    -:-: ÷⟜2 ≑
    ~:~: ∊ β‰ 
    ∾˘
    ,:,: ≍
    ;; ∾ ∾⟜(<⍟(1β‰₯≑))
    -˜(+Γ·β—‹(Γ—Β΄)⊒)1+β†•βˆ˜βŠ£
    /:/: ⍋ β‹βŠΈβŠ
    \:\: ⍒ β’βŠΈβŠ
    ↑
    {:{: ⊒˝
    {::{:: βŠ‘
    ↓
    }:}: Β―1βŠΈβ†“
    ":": β€’Fmt
    ?? β€’rand.Rangeβš‡0 β€’rand.Deal
    ⊐
    i:i: {𝕩-Λœβ†•1+2×𝕩} β‰ βˆ˜βŠ£-1+⌽⊸⊐
    x F˝∘GβŽ‰1β€Ώβˆž y
    F :. G{π•Š: 𝕨F𝕩; π•ŠβΌ: 𝕨G𝕩}F :. G{π•Š: 𝕨F𝕩; π•ŠβΌ: 𝕨G𝕩}
    <;._1<;._1 ((1-ΛœΒ¬Γ—+`)=⟜⊏⊘⊣)βŠ”βŠ’
    ⊘ Valences{𝔽𝕩;𝕨𝔾𝕩}{𝔽𝕩;𝕨𝔾𝕩} Apply 𝔽 if there's one argument but 𝔾 if there are two
    Block such as a function definition
    :: Block header
    ;; Block body separator
    ?? Predicate
    ↕ 10w?w? F x Subject
    + β‹ˆ -F?F? G H Function