aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorMarshall Lochbaum <mwlochbaum@gmail.com>2022-02-11 21:46:40 -0500
committerMarshall Lochbaum <mwlochbaum@gmail.com>2022-02-11 21:46:40 -0500
commit20bce97bb36da663e832d2aaccddfde846cc9c93 (patch)
treeddf125a95e9fc222e0f0130ccf9ec1c2afaa2d21 /docs
parentbe765e9b2195440fe40b927e804ea20f1a37bd24 (diff)
Is "creator has excessive interest in problems" a problem?
Diffstat (limited to 'docs')
-rw-r--r--docs/commentary/problems.html13
1 files changed, 11 insertions, 2 deletions
diff --git a/docs/commentary/problems.html b/docs/commentary/problems.html
index 2e69419e..ca964274 100644
--- a/docs/commentary/problems.html
+++ b/docs/commentary/problems.html
@@ -6,7 +6,7 @@
<div class="nav">(<a href="https://github.com/mlochbaum/BQN">github</a>) / <a href="../index.html">BQN</a> / <a href="index.html">commentary</a></div>
<h1 id="problems-with-bqn"><a class="header" href="#problems-with-bqn">Problems with BQN</a></h1>
<p>Every language has some issues that everyone can agree make programming harder. Sometimes there is a simple solution that has not yet been discovered; sometimes the problem is inherent to the language because it's caused by fundamental choices (or anywhere in between). Below are problems I have identified in BQN, ordered from what I consider the most severe to the least. This is independent of whether the issue can be solvedβ€”if it somehow went away, how much better would the language be?</p>
-<p>I've omitted problems that are obviously addressed by speculated extensions. Of course adding A fixes the problem &quot;doesn't have A&quot;. Problems that only exist in reference to some existing convention (e.g. unfamiliarity to APLers) are also left out, unless the convention manifests technically (Unicode support).</p>
+<p>This list is meant to be specific, so it addresses particular features rather than overall philosophy, and only includes missing functionality if some feature would be expected to provide it but doesn't. Problems that only exist in reference to some existing convention (e.g. unfamiliarity to APLers) are also left out, unless the convention manifests technically (Unicode support).</p>
<h3 id="empty-arrays-lose-type-information"><a class="header" href="#empty-arrays-lose-type-information">Empty arrays lose type information</a></h3>
<p>A pretty fundamental problem with dynamically-typed array languages: when computing something (say, a sum) that depends on all elements, if there are no elements then the structure of the result is indeterminate. Shape arithmetic means the shape of a cell is always known, except when using the Rank modifier so that every cell is computed independently. <a href="../doc/fill.html">Fills</a> are BQN's solution for deeper structure, but they're incomplete. They store only types and not data, but operations like Reshape that use data to determine type are common enough to make this unreliable.</p>
<h3 id="incoherent-monad-dyad-builtin-pairs"><a class="header" href="#incoherent-monad-dyad-builtin-pairs">Incoherent monad-dyad builtin pairs</a></h3>
@@ -25,6 +25,11 @@
<p>A programmer can call a modifier on either a syntactic function or subject, but there's no way to know within the modifier which syntax that operand had. Maybe this is a better design, but it doesn't feel quite right that <code><span class='Value'>f</span><span class='Modifier'>˜</span></code> is <code><span class='Value'>f</span></code>-Swap if <code><span class='Value'>f</span></code> has a function value. The subject syntax suggests it should be Constant. Instead the Constant modifier <code><span class='Modifier'>Λ™</span></code> has been added partially to mitigate this.</p>
<h3 id="group-doesnt-include-trailing-empty-groups"><a class="header" href="#group-doesnt-include-trailing-empty-groups">Group doesn't include trailing empty groups</a></h3>
<p>A length can now be specified either in an extra element in any rank-1 component of <code><span class='Value'>𝕨</span></code>, or by overtaking, since the result's fill element is an empty group. However, it still seems like it would be pretty easy to end up with a length error when a program using Group encounters unexpected data. It's a fundamental safety-convenience tradeoff, though, because specifying a length has to take more code in the general case.</p>
+<h3 id="variable-length-loops"><a class="header" href="#variable-length-loops">Variable-length loops</a></h3>
+<p>If the intended length of a loop isn't known before it begins, it can't be implemented with BQN primitives. The sensible way to handle such loops should be recursion… except that BQN implementations have stack limits. Some of them could support tail recursion, but for others it would be pretty hard and the result would be a lot of fragmentation. So uglier techniques are required, like <code><span class='Modifier2'>β€’_while_</span></code>, or the <a href="../doc/control.html#low-stack-version">low-stack recursion</a> that no one could be expected to invent.</p>
+<h3 id="an-unmatched-predicate-loses-locals"><a class="header" href="#an-unmatched-predicate-loses-locals">An unmatched predicate loses locals</a></h3>
+<p>For example <code><span class='Brace'>{</span><span class='Number'>0</span><span class='Function'>≀</span><span class='Value'>a</span><span class='Gets'>←</span><span class='Value'>𝕩</span><span class='Function'>-</span><span class='Number'>2</span> <span class='Head'>?</span> <span class='Value'>a</span> <span class='Head'>;</span> <span class='Function'>-</span><span class='Value'>a</span><span class='Brace'>}</span></code> doesn't compile: the <code><span class='Head'>;</span></code> separates bodies and the second one never defines <code><span class='Value'>a</span></code>. This can be fixed by enclosing in an immediate block, which might then require you to reassign some special names. So it's only ever a syntactic difficulty, but it gets pretty annoying at times.</p>
+<p>The root cause is repurposing the header-body system for an if-else thing (I found it quite surprising for this to be the worst issue caused by the approach). It can't be fixed with an extension, because the variable might really not be defined: consider <code><span class='Brace'>{</span><span class='Value'>l</span><span class='Function'>π•Š</span><span class='Value'>𝕩</span><span class='Head'>:</span> <span class='Value'>a</span><span class='Gets'>←</span><span class='Function'>+</span><span class='Modifier'>Β΄</span><span class='Value'>l</span><span class='Separator'>β‹„</span><span class='Function'>βŠ‘</span><span class='Value'>l</span><span class='Head'>?</span><span class='Number'>0</span> <span class='Head'>;</span> <span class='Value'>a</span><span class='Brace'>}</span></code>. If called with no left argument, it will go into the second case, never seeing an <code><span class='Value'>a</span><span class='Gets'>←</span></code>.</p>
<h3 id="hard-to-search-part-of-an-array-or-in-a-different-order"><a class="header" href="#hard-to-search-part-of-an-array-or-in-a-different-order">Hard to search part of an array or in a different order</a></h3>
<p>This includes index-of-last, and searching starting at a particular index, when the desired result indices are to the array to be seached <em>before</em> it is modified. Given indices <code><span class='Value'>i</span></code> into an array <code><span class='Value'>𝕨</span></code> (for example <code><span class='Function'>βŒ½β†•β‰ </span><span class='Value'>𝕨</span></code> or <code><span class='Value'>a</span><span class='Function'>+↕</span><span class='Value'>b</span></code>), this section can be searched with <code><span class='Paren'>(</span><span class='Value'>i</span><span class='Function'>βˆΎβ‰ </span><span class='Value'>𝕨</span><span class='Paren'>)</span><span class='Function'>⊏</span><span class='Modifier'>˜</span><span class='Paren'>(</span><span class='Value'>i</span><span class='Function'>⊏</span><span class='Value'>𝕨</span><span class='Paren'>)</span><span class='Function'>⊐</span><span class='Value'>𝕩</span></code>. But this is clunky and difficult for the implementation to optimize.</p>
<h3 id="right-to-left-multi-line-functions-go-upwards"><a class="header" href="#right-to-left-multi-line-functions-go-upwards">Right-to-left multi-line functions go upwards</a></h3>
@@ -43,6 +48,8 @@
<h3 id="deshape-and-reshape-cant-ignore-trailing-axes"><a class="header" href="#deshape-and-reshape-cant-ignore-trailing-axes">Deshape and Reshape can't ignore trailing axes</a></h3>
<p>If you want to repeat 3 major cells until there are 7 of them, or combine the first 4 axes of a rank-6 array, what's your best option? Nothing's too good: you could compute a full shape for Reshape, enclose cells and merge afterwards (for example <code><span class='Number'>7</span><span class='Modifier2'>⊸</span><span class='Function'>β₯Š</span><span class='Modifier2'>⌾</span><span class='Paren'>(</span><span class='Function'>&lt;</span><span class='Modifier'>˘</span><span class='Paren'>)</span></code>), use Select to reshape one axis to multiple, or use <code><span class='Function'>∾</span><span class='Modifier'>˝</span></code> to merge two axes (with possible empty-array issues). This is particularly dangerous with computed-length reshapes like <code><span class='Number'>2</span><span class='Ligature'>β€Ώ</span><span class='Modifier2'>∘</span><span class='Function'>β₯Š</span><span class='Value'>…</span></code>, since the idea of splitting off a length-2 axis from an array's first axis is generally useful, but this version has an implicit Deshape first.</p>
<p>J's Reshape analogue (<code><span class='Value'>$</span></code>) only ever applies to the first axis. I now think this is probably a better primitive to have overall, as <code><span class='Value'>$</span><span class='Modifier2'>⟜</span><span class='Function'>β₯Š</span></code> gives the APL behavior back and is rarely needed. It's not an intuitive pair with Deshape though.</p>
+<h3 id="long-trains-are-hard-for-humans-to-parse"><a class="header" href="#long-trains-are-hard-for-humans-to-parse">Long trains are hard for humans to parse</a></h3>
+<p>In a train consisting only of functions, the behavior of a function (whether it's applied directly to arguments, or to the results of other functions) is determined by its distance from the right side of the train. With a longer train it gets easy to lose track of whether the distance is even or odd. Sure, any bit of syntax gets hard when you put too much of it on a line, but it's notable that with trains this happens very quickly. The length where difficulty begins varies from about 4 to 8 parts, depending on the reader. A train of only functions is the worst case, as subjects can only go in one position and thus serve as anchors.</p>
<h3 id="prefixessuffixes-add-depth-and-windows-doesnt"><a class="header" href="#prefixessuffixes-add-depth-and-windows-doesnt">Prefixes/Suffixes add depth and Windows doesn't</a></h3>
<p>It's an awkward inconsistency. Prefixes and Suffixes have to have a nested result, but Windows doesn't have to be flat; it's just that making it nested ignores the fact that it does have an array structure.</p>
<h3 id="converting-a-function-expression-to-a-subject-is-tricky"><a class="header" href="#converting-a-function-expression-to-a-subject-is-tricky">Converting a function expression to a subject is tricky</a></h3>
@@ -95,7 +102,7 @@
<h3 id="monadic-argument-corresponds-to-left-for--and-"><a class="header" href="#monadic-argument-corresponds-to-left-for--and-">Monadic argument corresponds to left for <code><span class='Function'>/</span></code> and <code><span class='Function'>βŠ”</span></code></a></h3>
<p>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.</p>
<h3 id="high-rank-array-notation-awkwardness"><a class="header" href="#high-rank-array-notation-awkwardness">High-rank array notation awkwardness</a></h3>
-<p>The notation <code><span class='Value'>[]</span></code> will be added for high-rank arrays, the same as BQN's lists <code><span class='Bracket'>⟨⟩</span></code> 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: syntax with <code><span class='Function'>&lt;</span></code> and <code><span class='Value'>[]</span></code> would be complete over non-empty arrays.</p>
+<p>The notation <code><span class='Value'>[]</span></code> will be added for high-rank arrays, the same as BQN's lists <code><span class='Bracket'>⟨⟩</span></code> 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 <code><span class='Function'>&lt;</span></code> and <code><span class='Value'>[]</span></code> would be complete over non-empty arrays.</p>
<h3 id="assert-has-no-way-to-compute-the-error-message"><a class="header" href="#assert-has-no-way-to-compute-the-error-message">Assert has no way to compute the error message</a></h3>
<p>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 (<code><span class='Function'>!</span></code>) 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 <code><span class='Value'>𝕩</span></code> for the message. The issue generally applies to high-quality tools built in BQN, where giving the user good errors is a priority.</p>
<h3 id="monadic--versus-"><a class="header" href="#monadic--versus-">Monadic <code><span class='Function'>βŠ‘</span></code> versus <code><span class='Function'>&gt;</span></code></a></h3>
@@ -132,6 +139,8 @@
<p>There's a similar problem with <code><span class='Bracket'>⟨⟩</span></code> as a left argument to <code><span class='Function'>βŠ‘</span></code>: it could be a list of no indices, or a length-0 index. Currently it's treated as an index, causing errors when <code><span class='Value'>𝕨</span></code> is a variable-length list of indices. This could be mostly fixed with backwards compatibility by choosing the other way when <code><span class='Value'>𝕩</span></code> has nonzero rank.</p>
<h3 id="special-names-other-than-𝕣-cant-be-written-as-modifiers"><a class="header" href="#special-names-other-than-𝕣-cant-be-written-as-modifiers">Special names other than 𝕣 can't be written as modifiers</a></h3>
<p>I decided that it was better to allow <code><span class='Value'>𝕨</span><span class='Modifier2'>_m_</span><span class='Value'>𝕩</span></code> to work with no spaces than to allow <code><span class='Modifier2'>_</span><span class='Value'>𝕩</span></code> to be a modifier, and this rule also helps keep tokenization simple. But to apply <code><span class='Value'>𝕩</span></code> as a modifier you have to give it a different name. Could actually be a good thing in that it encourages you to stick to functions, as they're nicer in lots of other ways.</p>
+<h3 id="nothing-in-header-is-something-in-body"><a class="header" href="#nothing-in-header-is-something-in-body">Nothing in header is something in body</a></h3>
+<p>Since <code><span class='Nothing'>Β·</span></code> is used for an ignored value in destructuring, the header <code><span class='Nothing'>Β·</span><span class='Function'>π•Š</span><span class='Value'>𝕩</span><span class='Head'>:</span></code> indicates that <code><span class='Value'>𝕨</span></code> has some value, that is, that it's not <code><span class='Nothing'>Β·</span></code>.</p>
<h3 id="exact-result-of-power-is-unspecified"><a class="header" href="#exact-result-of-power-is-unspecified">Exact result of Power is unspecified</a></h3>
<p>The other arithmetic functions round to nearest, and compound functions such as <code><span class='Value'>βŠ₯</span></code> have been removed. But Power makes no guarantees, and the result could change over time based on different special code. Dyadic logarithm is similar, but expected because of its inverse status.</p>
<h3 id="unclear-primitive-names"><a class="header" href="#unclear-primitive-names">Unclear primitive names</a></h3>