logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·01.11.26
← Back to news

Spatial languages: Writing code in 2D

Original article
shukla.io|69 points|24 comments|by BinRoo|Jul 22, 2026

Spatial Languages: Coding in Two Dimensions

By Nishant Shukla | Published: 2026-07-22

Why do we insist that expressions must be read strictly from left to right on a single line? Linearity is the only way to code.

Diagram of 1D vs 2D code layout

For decades, we have confined our logic to 1D space. But what happens when we unlock the yyy-axis? To make this a reality, we simply need a parser and an IDE that can handle spatial coordinates.

The Problem with 3-Arity Functions

In most modern languages, we rarely encounter true 3-arity functions written in infix notation. Take Python, for example:

x and y and z

While this looks like a single operation, it is actually just a composition of multiple binary functions. Even Python's shorthand for (a and b) and (b and c) →\rightarrow→ a and b and c follows this same binary logic.

To illustrate the need for a spatial approach, let's look at a custom operator I've been developing called andFlip.

Defining andFlip

The logic is simple: it takes two inputs and flips the state of a target if both inputs are true.

def andFlip(args):
    # args[0] and args[1] are inputs, args[2] is the target
    if args[0] and args[1]:
        args[2] = not args[2]
    return args[2]

Two Ways to Implement This

Method 1: The Conventional Approach We could use currying. The infix operator @@ takes two arguments and returns a function that waits for the third: (a1 @@ a2) a3 It works, but it's boring.

Method 2: The Spatial Approach What if we used the vertical axis? I envision a notation where the target sits above or below the operator:

Target a3\text{Target } a_3Target a3​ a1 @@ a2a_1 \text{ @@ } a_2a1​ @@ a2​

This makes chaining significantly more intuitive. Consider this flow: t1=0t_1 = 0t1​=0 t2=0t_2 = 0t2​=0 t1t2t_1 \quad t_2t1​t2​ (x @@ y) @@ z(x \text{ @@ } y) \text{ @@ } z(x @@ y) @@ z

In this chain, x @@ y toggles t1t_1t1​, and then t1 @@ zt_1 \text{ @@ } zt1​ @@ z toggles t2t_2t2​. This creates a successful three-way toggle where t2t_2t2​ only flips if x,y, and zx, y, \text{ and } zx,y, and z are all true.

Real-World Application: The Chicken Coop

I applied this logic to the automatic door of my chicken coop. The system uses a microcontroller to monitor three specific conditions:

VariableConditionDescription
xPhotometerHas the dusk/dawn threshold been crossed?
yStabilityHas the reading remained steady for 5 minutes?
zRecencyHas the gate avoided toggling too recently?

The Logic: The door should only toggle when all three conditions align. If a cloud passes by, x might trigger, but it won't satisfy y, so the door remains still.

The controller uses the chained expression: t1=0,t2=0t_1 = 0, t_2 = 0t1​=0,t2​=0 t1t2t_1 \quad t_2t1​t2​ (x @@ y) @@ z(x \text{ @@ } y) \text{ @@ } z(x @@ y) @@ z

Why not just use x and y and z? Because a standard and mirrors the sensors; the door would swing back the moment a condition failed. The @@ operator toggles in place, meaning the door moves once and stays put, leaving the intermediate result in t1t_1t1​ (a "scratch bit").

Vertical Chaining and Logic Puzzles

Because t1t_1t1​ is mutable, this isn't a "pure function." This leads us to some interesting spatial puzzles.

  • Puzzle 1: Resetting t1t_1t1​ to 0 How do we reset t1t_1t1​ to 0 within the same expression to make it appear immutable? Since repeating the toggle resets the value, we can wrap the expression:

    t1=0,t2=0t_1 = 0, t_2 = 0t1​=0,t2​=0 (x @@ y) @@ z(x \text{ @@ } y) \text{ @@ } z(x @@ y) @@ z t1t2t_1 \quad t_2t1​t2​ (x @@ y)(x \text{ @@ } y)(x @@ y)

    Data flows top-to-bottom and left-to-right. If xxx and yyy are true, the first x @@ y flips t1t_1t1​, and the second one flips it back. This is a spatial representation of the CCCX gate in quantum computing.

  • Puzzle 2: The "Dirty" Ancilla What if t1t_1t1​ starts with an unknown value nnn? t1=n,t2=0t_1 = n, t_2 = 0t1​=n,t2​=0 t1t2t_1 \quad t_2t1​t2​ (x @@ y) @@ z(x \text{ @@ } y) \text{ @@ } z(x @@ y) @@ z

    The previous trick fails here. If t1=1t_1=1t1​=1 and all inputs are true, t2t_2t2​ stays 0 (incorrect). The solution is a mirrored 2D structure:

    t1=n,t2=0t_1 = n, t_2 = 0t1​=n,t2​=0 t1t2t_1 \quad t_2t1​t2​ (x @@ y) @@ z(x \text{ @@ } y) \text{ @@ } z(x @@ y) @@ z t1t2t_1 \quad t_2t1​t2​ (x @@ y) @@ z(x \text{ @@ } y) \text{ @@ } z(x @@ y) @@ z

This resets t1t_1t1​ to nnn while correctly calculating t2t_2t2​. This mimics resetting a borrowed ancilla in quantum circuits.

Comparison with Qiskit

In Qiskit, this same logic looks like this:

from qiskit import QuantumCircuit, QuantumRegister

x = QuantumRegister(1, 'x')
y = QuantumRegister(1, 'y')
z = QuantumRegister(1, 'z')
t1 = QuantumRegister(1, 't1') # Unknown state n
t2 = QuantumRegister(1, 't2') # Target, starts at 0

qc = QuantumCircuit(x, y, z, t1, t2)
qc.ccx(x, y, t1)
qc.ccx(t1, z, t2)
qc.ccx(x, y, t1)
qc.ccx(t1, z, t2)

The Qiskit code is harder to parse because of the constant variable tracking. A circuit diagram is better, but not always scalable.

Defining New Spatial Operators

We can define new operators using the := symbol, where the left side is a spatial shape and the right side is its rewrite rule.

Let's define a single @ operator: x @ y flips yyy if xxx is true. Now we can build the maj (majority) operator:

    b  maj  c
(a @ b) @@ (a @ c)
    a := a

This operation flips bbb and ccc if aaa is true, then flips aaa if both resulting sides are true. Mathematically, this computes: maj(a,b,c)=a⊕((a⊕b)∧(a⊕c))\text{maj}(a, b, c) = a \oplus ((a \oplus b) \land (a \oplus c))maj(a,b,c)=a⊕((a⊕b)∧(a⊕c))

This is the MAJ gate used in quantum ripple-carry adders to determine the next carry bit. Note that maj leaves bbb and ccc "dirty" (holding a⊕ba \oplus ba⊕b and a⊕ca \oplus ca⊕c), similar to our borrowed ancilla problem.