Skip to content

Commit

Permalink
Elaborate more on how lexical scoping creates mutable values
Browse files Browse the repository at this point in the history
  • Loading branch information
mlochbaum committed Jul 9, 2022
1 parent f2a5437 commit 36c94fe
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 6 deletions.
28 changes: 26 additions & 2 deletions doc/lexical.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,32 @@ When a variable's modified, functions with access to it see the new value. They
factor ↩ 5
Mul 6 # A new result

Only code with access to a variable can modify it! This means that if none of the code in a variable's scope modifies it, then the variable is a constant in each environment that contains it (not necessarily across environments). That is, constant once it's defined: remember that it's still possible to get an error if the variable is accessed before being defined.
Only source code with access to a variable can modify it! This means that if none of the code in a variable's scope modifies it, then the variable is constant. That is, constant once it's defined: remember that it's still possible to get an error if the variable is accessed before being defined.

{ { a } ⋄ a←4 }

With lexical scoping, variable mutation automatically leads to mutable data. This is because a function or modifier that depends on the variable value changes its behavior when the variable changes. For further discussion see the documentation on [mutable objects](oop.md#mutability).
With lexical scoping, variable mutation automatically leads to mutable data. This is because a function or modifier that depends on the variable value changes its behavior when the variable changes. So do objects; this slightly more concrete case is discussed [here](oop.md#mutability). The behavior change is observed by calling operations, and by accessing object fields. These are the only two actions that might behave differently when applied to the same values!

### Aliasing

Mutable values exhibits *aliasing*. This means that when two variables refer to the same mutable value (or two copies of it exist generally), changes to one also affect the other.

record ← { r←⟨⟩ ⋄ { r ∾↩ <𝕩 } }
Record ∞

Record2 ← Record # Copy the function
Record2 "new"

Record 0 # The added value "new" is seen here as well

This could be said to conflict with arrays, where two variables might be copies of the same array but a change to one doesn't affect the other.

copy_a ← copy_b ← "array"

copy_b 'b'⌾⊑↩

copy_a

But that's not really what's happening. Aliasing has nothing to do with variables: it's a property of mutation, and there's no such thing as array mutation. `'b'⌾⊑` creates a new but related array. And `` changes the value of `copy_b`, not *its* value `"array"`. Similarly, if we wrote `record2 ↩ @` then nothing would happen to `record`.

You can tell whether two mutable values alias each other using Match (``), because that's how it defines [block equality](match.md#block-equality). However, aliasing isn't the only way one mutable value can affect another: two functions might refer to the same variable, for instance. I think the idea that the function itself is mutable can cause some confusion, and sometimes prefer to think at a lower level—that variables don't belong to the function, but the function just knows about them. So a function is more like a file name (path) than a file. It's a static thing, but using it (calling the function or accessing the file) reads outside information that can change over time.
2 changes: 1 addition & 1 deletion doc/oop.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ A stack is a particularly simple class to make because its state can be represen

## Mutability

An object is one way to transform *variable mutation* `↩` into *mutable data*. These are two different concepts: `↩` changes which value is attached to a *name* in a scope, while mutable data means that the behavior of a particular *value* can change. But if a value is linked to a scope (for an object, the scope that contains its fields), then variable mutation in that scope can change the value's behavior. In fact, in BQN this is the only way to create mutable data. Which doesn't mean it's rare: functions, modifiers, and namespaces are all potentially mutable. The difference between objects and the operations is just a matter of syntax. Mutability in operations can only be observed by calling them. For instance `F 10` or `-_m` could return a different result even if the variables involved don't change value. Mutability in an object can only be observed by accessing a member, meaning that `obj.field` or `⟨field⟩←obj` can yield different values over the course of a program even if `obj` is still the same object.
An object is one way to transform *variable mutation* `↩` into [*mutable data*](lexical.md#mutation). These are two different concepts: `↩` changes which value is attached to a *name* in a scope, while mutable data means that the behavior of a particular *value* can change. But if a value is linked to a scope (for an object, the scope that contains its fields), then variable mutation in that scope can change the value's behavior. In fact, in BQN this is the only way to create mutable data. Which doesn't mean it's rare: functions, modifiers, and namespaces are all potentially mutable. The difference between objects and the operations is just a matter of syntax. Mutability in operations can only be observed by calling them. For instance `F 10` or `-_m` could return a different result even if the variables involved don't change value. Mutability in an object can only be observed by accessing a member, meaning that `obj.field` or `⟨field⟩←obj` can yield different values over the course of a program even if `obj` is still the same object.

Let's look at how mutability plays out in an example class for a single-ended queue. This queue works by linking new nodes to the tail `t` of the queue, and detaching nodes from the head `h` when requested (a detached node will still point to `h`, but nothing in the queue points to *it*, so it's unreachable and will eventually be garbage collected). Each node has some data `v` and a single node reference `n` directed tailwards; in a double-ended queue or more complicated structure it would have more references. You can find every mutable variable in the queue by searching for ``, which shows that `t` and `h` in the queue, and `n` in a node, may be mutated. It's impossible for the other variables to change value once they're assigned.

Expand Down
28 changes: 26 additions & 2 deletions docs/doc/lexical.html
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,32 @@ <h2 id="mutation"><a class="header" href="#mutation">Mutation</a></h2>
<span class='Function'>Mul</span> <span class='Number'>6</span> <span class='Comment'># A new result
</span>30
</pre>
<p>Only code with access to a variable can modify it! This means that if none of the code in a variable's scope modifies it, then the variable is a constant in each environment that contains it (not necessarily across environments). That is, constant once it's defined: remember that it's still possible to get an error if the variable is accessed before being defined.</p>
<p>Only source code with access to a variable can modify it! This means that if none of the code in a variable's scope modifies it, then the variable is constant. That is, constant once it's defined: remember that it's still possible to get an error if the variable is accessed before being defined.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=eyB7IGEgfSDii4QgYeKGkDQgfQ==">↗️</a><pre> <span class='Brace'>{</span> <span class='Brace'>{</span> <span class='Value'>a</span> <span class='Brace'>}</span> <span class='Separator'></span> <span class='Value'>a</span><span class='Gets'></span><span class='Number'>4</span> <span class='Brace'>}</span>
<span class='Error'>Error: Reading variable before its defined</span>
</pre>
<p>With lexical scoping, variable mutation automatically leads to mutable data. This is because a function or modifier that depends on the variable value changes its behavior when the variable changes. For further discussion see the documentation on <a href="oop.html#mutability">mutable objects</a>.</p>
<p>With lexical scoping, variable mutation automatically leads to mutable data. This is because a function or modifier that depends on the variable value changes its behavior when the variable changes. So do objects; this slightly more concrete case is discussed <a href="oop.html#mutability">here</a>. The behavior change is observed by calling operations, and by accessing object fields. These are the only two actions that might behave differently when applied to the same values!</p>
<h3 id="aliasing"><a class="header" href="#aliasing">Aliasing</a></h3>
<p>Mutable values exhibits <em>aliasing</em>. This means that when two variables refer to the same mutable value (or two copies of it exist generally), changes to one also affect the other.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=cmVjb3JkIOKGkCB7IHLihpDin6jin6kg4ouEIHsgciDiiL7ihqkgPPCdlakgfSB9ClJlY29yZCDiiJ4KClJlY29yZDIg4oaQIFJlY29yZCAgIyBDb3B5IHRoZSBmdW5jdGlvbgpSZWNvcmQyICJuZXciCgpSZWNvcmQgMCAgICMgVGhlIGFkZGVkIHZhbHVlICJuZXciIGlzIHNlZW4gaGVyZSBhcyB3ZWxs">↗️</a><pre> <span class='Value'>record</span> <span class='Gets'></span> <span class='Brace'>{</span> <span class='Value'>r</span><span class='Gets'></span><span class='Bracket'>⟨⟩</span> <span class='Separator'></span> <span class='Brace'>{</span> <span class='Value'>r</span> <span class='Function'></span><span class='Gets'></span> <span class='Function'>&lt;</span><span class='Value'>𝕩</span> <span class='Brace'>}</span> <span class='Brace'>}</span>
<span class='Function'>Record</span> <span class='Number'></span>
⟨ ∞ ⟩

<span class='Function'>Record2</span> <span class='Gets'></span> <span class='Function'>Record</span> <span class='Comment'># Copy the function
</span> <span class='Function'>Record2</span> <span class='String'>&quot;new&quot;</span>
⟨ ∞ "new" ⟩

<span class='Function'>Record</span> <span class='Number'>0</span> <span class='Comment'># The added value &quot;new&quot; is seen here as well
</span>⟨ ∞ "new" 0 ⟩
</pre>
<p>This could be said to conflict with arrays, where two variables might be copies of the same array but a change to one doesn't affect the other.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=Y29weV9hIOKGkCBjb3B5X2Ig4oaQICJhcnJheSIKCmNvcHlfYiAnYifijL7iipHihqkKCmNvcHlfYQ==">↗️</a><pre> <span class='Value'>copy_a</span> <span class='Gets'></span> <span class='Value'>copy_b</span> <span class='Gets'></span> <span class='String'>&quot;array&quot;</span>

<span class='Value'>copy_b</span> <span class='String'>'b'</span><span class='Modifier2'></span><span class='Function'></span><span class='Gets'></span>
"brray"

<span class='Value'>copy_a</span>
"array"
</pre>
<p>But that's not really what's happening. Aliasing has nothing to do with variables: it's a property of mutation, and there's no such thing as array mutation. <code><span class='String'>'b'</span><span class='Modifier2'></span><span class='Function'></span></code> creates a new but related array. And <code><span class='Gets'></span></code> changes the value of <code><span class='Value'>copy_b</span></code>, not <em>its</em> value <code><span class='String'>&quot;array&quot;</span></code>. Similarly, if we wrote <code><span class='Value'>record2</span> <span class='Gets'></span> <span class='String'>@</span></code> then nothing would happen to <code><span class='Value'>record</span></code>.</p>
<p>You can tell whether two mutable values alias each other using Match (<code><span class='Function'></span></code>), because that's how it defines <a href="match.html#block-equality">block equality</a>. However, aliasing isn't the only way one mutable value can affect another: two functions might refer to the same variable, for instance. I think the idea that the function itself is mutable can cause some confusion, and sometimes prefer to think at a lower level—that variables don't belong to the function, but the function just knows about them. So a function is more like a file name (path) than a file. It's a static thing, but using it (calling the function or accessing the file) reads outside information that can change over time.</p>
2 changes: 1 addition & 1 deletion docs/doc/oop.html
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ <h2 id="classes"><a class="header" href="#classes">Classes</a></h2>
</pre>
<p>A stack is a particularly simple class to make because its state can be represented efficiently as a BQN value. Other data structures don't allow this, and will often require an extra <code><span class='Function'>Node</span></code> class in an implementation—see <code><span class='Function'>MakeQueue</span></code> below.</p>
<h2 id="mutability"><a class="header" href="#mutability">Mutability</a></h2>
<p>An object is one way to transform <em>variable mutation</em> <code><span class='Gets'></span></code> into <em>mutable data</em>. These are two different concepts: <code><span class='Gets'></span></code> changes which value is attached to a <em>name</em> in a scope, while mutable data means that the behavior of a particular <em>value</em> can change. But if a value is linked to a scope (for an object, the scope that contains its fields), then variable mutation in that scope can change the value's behavior. In fact, in BQN this is the only way to create mutable data. Which doesn't mean it's rare: functions, modifiers, and namespaces are all potentially mutable. The difference between objects and the operations is just a matter of syntax. Mutability in operations can only be observed by calling them. For instance <code><span class='Function'>F</span> <span class='Number'>10</span></code> or <code><span class='Function'>-</span><span class='Modifier'>_m</span></code> could return a different result even if the variables involved don't change value. Mutability in an object can only be observed by accessing a member, meaning that <code><span class='Value'>obj.field</span></code> or <code><span class='Bracket'></span><span class='Value'>field</span><span class='Bracket'></span><span class='Gets'></span><span class='Value'>obj</span></code> can yield different values over the course of a program even if <code><span class='Value'>obj</span></code> is still the same object.</p>
<p>An object is one way to transform <em>variable mutation</em> <code><span class='Gets'></span></code> into <a href="lexical.html#mutation"><em>mutable data</em></a>. These are two different concepts: <code><span class='Gets'></span></code> changes which value is attached to a <em>name</em> in a scope, while mutable data means that the behavior of a particular <em>value</em> can change. But if a value is linked to a scope (for an object, the scope that contains its fields), then variable mutation in that scope can change the value's behavior. In fact, in BQN this is the only way to create mutable data. Which doesn't mean it's rare: functions, modifiers, and namespaces are all potentially mutable. The difference between objects and the operations is just a matter of syntax. Mutability in operations can only be observed by calling them. For instance <code><span class='Function'>F</span> <span class='Number'>10</span></code> or <code><span class='Function'>-</span><span class='Modifier'>_m</span></code> could return a different result even if the variables involved don't change value. Mutability in an object can only be observed by accessing a member, meaning that <code><span class='Value'>obj.field</span></code> or <code><span class='Bracket'></span><span class='Value'>field</span><span class='Bracket'></span><span class='Gets'></span><span class='Value'>obj</span></code> can yield different values over the course of a program even if <code><span class='Value'>obj</span></code> is still the same object.</p>
<p>Let's look at how mutability plays out in an example class for a single-ended queue. This queue works by linking new nodes to the tail <code><span class='Value'>t</span></code> of the queue, and detaching nodes from the head <code><span class='Value'>h</span></code> when requested (a detached node will still point to <code><span class='Value'>h</span></code>, but nothing in the queue points to <em>it</em>, so it's unreachable and will eventually be garbage collected). Each node has some data <code><span class='Value'>v</span></code> and a single node reference <code><span class='Value'>n</span></code> directed tailwards; in a double-ended queue or more complicated structure it would have more references. You can find every mutable variable in the queue by searching for <code><span class='Gets'></span></code>, which shows that <code><span class='Value'>t</span></code> and <code><span class='Value'>h</span></code> in the queue, and <code><span class='Value'>n</span></code> in a node, may be mutated. It's impossible for the other variables to change value once they're assigned.</p>
<pre><span class='Function'>MakeQueue</span> <span class='Gets'></span> <span class='Brace'>{</span><span class='Value'>𝕤</span>
<span class='Value'>t</span><span class='Gets'></span><span class='Value'>h</span><span class='Gets'></span><span class='Value'>e</span><span class='Gets'></span><span class='Brace'>{</span><span class='Function'>SetN</span><span class='Gets'></span><span class='Brace'>{</span><span class='Value'>h</span><span class='Gets'></span><span class='Value'>𝕩</span><span class='Brace'>}}</span>
Expand Down

0 comments on commit 36c94fe

Please sign in to comment.