<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Future Office Labs</title>
    <link>https://futureofficelabs.com/</link>
    <description>Blog posts from Future Office Labs</description>
    <atom:link href="https://futureofficelabs.com/blog/feed.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>DAGEdit, Now in Clojure</title>
      <link>https://futureofficelabs.com/blog/dagedit-in-clojure.html</link>
      <guid isPermaLink="true">https://futureofficelabs.com/blog/dagedit-in-clojure.html</guid>
      <pubDate>Fri, 06 Nov 2026 00:00:00 +0000</pubDate>
      <description>&lt;p&gt;A quick update on the &lt;a href="https://futureofficelabs.com/blog/keyboard-dag-editing.html"&gt;keyboard DAG editor&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The first version was a JavaScript experiment in the browser. I've now rewritten it in Clojure. The main version is a TUI: it runs in the terminal, edits a graph saved as a &lt;code&gt;.json&lt;/code&gt; file, and opens a node's details in your &lt;code&gt;$EDITOR&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The editor itself is written once, as shared Clojure / ClojureScript code that draws the screen as a grid of characters. The terminal version prints that grid. And the same code, compiled to JavaScript, is the new web version, which looks just like the terminal.&lt;/p&gt;
&lt;p&gt;&lt;img alt="DAGEdit in the browser, with the Graphviz view pinned on the right" src="https://futureofficelabs.com/assets/blog/dagedit-in-clojure/dagedit-web.png"&gt;&lt;/p&gt;
&lt;p&gt;The web version has one thing the terminal doesn't: a Graphviz drawing of the DAG. Ctrl+P pops it up, and P pins it, so the editor is on the left and the graph is on the right, redrawn as you type. From there you can download the graph as SVG, PNG, DOT or Cypher.&lt;/p&gt;
&lt;p&gt;Your graph is saved in the browser as you go. Ctrl+S downloads it as a &lt;code&gt;.json&lt;/code&gt; file, and Alt+O opens a &lt;code&gt;.json&lt;/code&gt; or a Graphviz &lt;code&gt;.dot&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Some of the keys have changed since the first post:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl+F&lt;/td&gt;
&lt;td&gt;fork: a new connected node in the next column&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl+Y, then Ctrl+V&lt;/td&gt;
&lt;td&gt;copy a node's output, and paste it as an input on another node (a merge)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl+K&lt;/td&gt;
&lt;td&gt;delete a node, joining up the nodes either side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl+E&lt;/td&gt;
&lt;td&gt;the connections palette&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;F1 lists them all.&lt;/p&gt;
&lt;p&gt;Try it here: &lt;a href="https://futureofficelabs.com/apps/dageditor/"&gt;DAGEdit&lt;/a&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>The Worst Idea in Computer Science</title>
      <link>https://futureofficelabs.com/blog/direct-manipulation.html</link>
      <guid isPermaLink="true">https://futureofficelabs.com/blog/direct-manipulation.html</guid>
      <pubDate>Fri, 06 Nov 2026 00:00:00 +0000</pubDate>
      <description>&lt;p&gt;I sometimes call "direct manipulation" the "worst idea in computer science". It's a brilliant idea. And precisely because it's so good, it's become so pernicious.&lt;/p&gt;
&lt;p&gt;But hopefully, its reign of despottery is ending.&lt;/p&gt;
&lt;p&gt;The kernel of truth inside the hype of the "AI revolution" is that we are moving to natural language as the main user interface paradigm for computers. There is now very little need for the GUI. For pointing at things with mice. For scrolling. For direct manipulation.&lt;/p&gt;
&lt;p&gt;The main advantage that GUIs claimed over text terminals was "discovery". I didn't know what my software could do until I looked at its menu options. But now discovery is as easy as asking it "is there a way to do X?"&lt;/p&gt;
&lt;p&gt;You can see the attraction of the natural language paradigm in both the popular enthusiasm around agents like OpenClaw. And in the enthusiasm for the TUI and tiling window managers among the geeks. People who are keen users of computers are overwhelmingly voting for natural language UX. And the future of software is "agentic" this way. Natural language UI. And at least enough knowledge and memory in it that a user can meaningfully delegate long term and batch processing work via natural language.&lt;/p&gt;
&lt;p&gt;As an aside, it's fascinating to observe that we've been so immersed and constrained by the "direct manipulation" paradigm for end users, that batch processing, processing on a timer or cronjob, and event driven processing all seem miraculous to most users. These possibilities have been in our operating systems since the 1970s. But the direct manipulation paradigm obscured the possibility from all but the most technical. If LLMs do nothing but bring the masses the virtues of "indirect manipulation" they'll have been worth it. Over the past 40 years, the direct manipulation paradigm enshitified from  helpful (here's what you can do) to manipulative (here's another thing to grab your attention, keep scrolling)&lt;/p&gt;
&lt;p&gt;Chatbots, at least for the moment, are a reset to a self-service, user pulls what they want, model. Rather than the UI pushing more distraction at you. (Though beware! I'm sure there are people working on reversing that.) The next OS will be made of chatbots in the way current UIs are made of windows. Your software needs to fit into that paradigm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This is something I wrote on LinkedIn in response to &lt;a href="https://lnkd.in/p/e-kSa5Zm"&gt;this&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>A DSL for UI Layouts</title>
      <link>https://futureofficelabs.com/blog/a-dsl-for-ui-layouts.html</link>
      <guid isPermaLink="true">https://futureofficelabs.com/blog/a-dsl-for-ui-layouts.html</guid>
      <pubDate>Sat, 29 Nov 2025 00:00:00 +0000</pubDate>
      <description>&lt;p&gt;I know this is probably more me than you, dear reader. But I just don't like working in two dimensions.&lt;/p&gt;
&lt;p&gt;Laying out things like web-page designs or UI designs is a faff. If I use the mouse it's just OK. But a bit laborious.&lt;/p&gt;
&lt;p&gt;On the other hand, declarative HTML-like and templating languages SHOULD be fine. But can become hugely complicated. &lt;/p&gt;
&lt;p&gt;That's because&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a) in something like html, the layout is verbose anyway. &lt;/li&gt;
&lt;li&gt;b) with CSS, some layout can end up in separate CSS files, which means you are looking all over the place to see what's going on &lt;/li&gt;
&lt;li&gt;c) layout information is often muddled up with the content that's being laid out.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now the separation of concerns which the HTML / CSS distinction is meant to represent is a fine thing. But in practice the two don't come apart so easily.&lt;/p&gt;
&lt;p&gt;And in other ways of building forms, imperatively, in more traditional GUI libraries, we don't even have the virtues claimed for HTML. And again, the layout and the rest of what happens &lt;em&gt;in&lt;/em&gt; the layout can get very intertwingled.&lt;/p&gt;
&lt;p&gt;So I've been thinking for a while about a nice simple DSL that should represent &lt;strong&gt;just&lt;/strong&gt; layout information. Just how a space should be carved into regions.&lt;/p&gt;
&lt;p&gt;I played with an early prototype of this as an example in &lt;a href="https://github.com/interstar/Neh-Thalggu/"&gt;Neh-Thalggu&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;This week I've been exploring &lt;a href="https://www.canva.dev/docs/apps/"&gt;Canva apps&lt;/a&gt; from a developer perspective. I've been playing with putting some Patterning-derived apps there. But while doing so, I wondered whether my DSL would also work for making forms there. &lt;/p&gt;
&lt;p&gt;And so I'm also playing with expanding this UI DSL a bit. To define, at first, paper forms.&lt;/p&gt;
&lt;p&gt;The core idea is that all layouts are effectively blocks that are nested and arranged either horizontally or vertically. So why not use two different kinds of brackets, one for each?&lt;/p&gt;
&lt;p&gt;In my DSL we use square brackets to represent vertical flow. So &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[header body footer]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;means header, body and footer flowed vertically.&lt;/p&gt;
&lt;p&gt;Angle brackets represent horizontal flow. So &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[header &amp;lt;col1 col2&amp;gt; footer]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;turns the second section into two columns. And we can go on, nesting as much as we like.&lt;/p&gt;
&lt;pre class="code"&gt;&lt;code&gt;[header 
   &amp;lt;maincolumn
     [sidebar1 sidebar2 sidebar3]&amp;gt;
 footer] &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now it's important to note here that the language doesn't assign any meaning to the labels we give. These are really just symbols that mean whatever the user of the language wants them to mean. In the Canva app I'm working on, they just become text headers. While in a code-generator or Neh-Thalggu or embedded in a GUI framework, they might be references or tags that can be matched to specific GUI components.&lt;/p&gt;
&lt;p&gt;Even with this simple language we can already say (and read) things clearly and concisely, that would otherwise be lost in the thickets of other functionality.&lt;/p&gt;
&lt;p&gt;But we do need more control over relative sizes of the boxes. By default they are evenly divided. So if there are three things in a vertical flow, we assign a third of the space to each.&lt;/p&gt;
&lt;p&gt;We can change this by adding a suffix to a label. This suffix represents (roughly) the ratio of the space this element should take up compared to its default. Let's suppose we think the header and the footer don't need to take up their full third of the screen. But only a proportion of that allotted space. We'd write something like this&lt;/p&gt;
&lt;pre class="code"&gt;&lt;code&gt;[header(1/2) 
   &amp;lt;maincolumn
     [sidebar1 sidebar2 sidebar3]&amp;gt;
 footer(1/2)] &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tells the layout engine to halve the space for both header and footer to the equivalent of 1/6 of the total vertical space each. Leaving 4/6 for the main section. In practice it doesn't work out exactly like this. Each specific request for space impacts the share that other sections get. But it's close enough that, with a small amount of fiddling, you can find reasonably nice proportions for each section.&lt;/p&gt;
&lt;p&gt;If we want a section to claim more of the available space, we put a ratio that's greater than 1. In this example, the maincolumn demands 3 times its half of the width adding up to 3/4.&lt;/p&gt;
&lt;pre class="code"&gt;&lt;code&gt;[header(1/2) 
 &amp;lt;maincolumn(3)
      [sidebar1 sidebar2 sidebar3]&amp;gt; 
 footer(1/3)]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here's how the above gets rendered.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://futureofficelabs.com/assets/blog/a-dsl-for-ui-layouts/layout.png"&gt;&lt;/p&gt;
&lt;p&gt;So. I think this is plausible. If I need to make simple SVG page templates, with labelled boxes I can. And I &lt;s&gt;think&lt;/s&gt; hope this might well be a useful little tool in Canva. (Though with the caveat I'm not sure the average Canva user will particularly like working this way.)&lt;/p&gt;
&lt;p&gt;The bigger test will come when I get back to the suite of music composition apps I'm currently working on, where I need to define a more complex GUI. I'm hoping I can use this to generate the layout code for that.  But we'll see.&lt;/p&gt;
&lt;p&gt;In the meantime, you can play with it &lt;a href="https://futureofficelabs.com/apps/ui/index.html"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And at some point the revised, improved version of this language will find its way back to Neh Thalggu as an example DSL there.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Voice Labelled Graph Editor</title>
      <link>https://futureofficelabs.com/blog/voice-labelled-graph-editor.html</link>
      <guid isPermaLink="true">https://futureofficelabs.com/blog/voice-labelled-graph-editor.html</guid>
      <pubDate>Tue, 25 Nov 2025 00:00:00 +0000</pubDate>
      <description>&lt;p&gt;I am, &lt;a href="https://futureofficelabs.com/blog/keyboard-dag-editing/"&gt;as I may have mentioned&lt;/a&gt;, a bit obsessed with graph editors.&lt;/p&gt;
&lt;p&gt;DAGs and other graphs are hugely important. And we want to tell the computers about them all the time.&lt;/p&gt;
&lt;p&gt;However they don't map conveniently on to text.&lt;/p&gt;
&lt;p&gt;And this infuriates me because I &lt;strong&gt;like&lt;/strong&gt; text. A lot. I think text is the greatest UI known to humanity. That's why we evolved to speak not sign to each other. And alphabets not ideograms took over the world.&lt;/p&gt;
&lt;p&gt;So it pains me, deep in my soul, that when making graph-shaped things that I love - from synthesisers to dataflow pipelines and process models - that text (and lexical scoping) doesn't quite work for them.&lt;/p&gt;
&lt;p&gt;Perhaps we just DO need to draw graphs with something else. I need to be flexible on this.&lt;/p&gt;
&lt;p&gt;But what's &lt;strong&gt;really&lt;/strong&gt; annoying is continuously having to switch between mouse and keyboard when editing a graph. Mouse for the topology, and keyboard for the labelling of nodes.&lt;/p&gt;
&lt;p&gt;So my &lt;a href="https://futureofficelabs.com/blog/keyboard-dag-editing/"&gt;last project in this area&lt;/a&gt; was the Keyboard Only DAG editor. And that works pretty well. (As an idea. There are a couple of bugs in the implementation)&lt;/p&gt;
&lt;p&gt;But what about turning the problem around? Instead of staying on the keyboard, being able to stay only with the mouse or drawing tool? The issue here is how to label the nodes. To tell the computer what each is meant to be and what extra data it holds or function it executes.&lt;/p&gt;
&lt;p&gt;Well, if my hands are occupied with the mouse, what about my voice? Are we yet in a stage where we can use voice to label nodes?&lt;/p&gt;
&lt;p&gt;I made a simple experiment using the &lt;a href="https://alphacephei.com/vosk/"&gt;Vosk&lt;/a&gt; voice recognition model, that runs completely in the browser.&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/ZiFr-UKLO4I?si=N5PjbOz_zAOQijd9" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;The fact that it's do-able in the browser is one of those things that still impresses me but I need to recalibrate my expectations to get used to. But it's still not great. &lt;/p&gt;
&lt;p&gt;Nevertheless, I can imagine this combination of drawing + voice might well become a comfortable way of working for many people on many tasks. To me neither drawing nor voice have the virtues of typing text. But combined they become far more compelling.&lt;/p&gt;
&lt;p&gt;When I have the next, better, demo of something like this, I'll put it up to play with.&lt;/p&gt;</description>
    </item>
    <item>
      <title>The Douat for Patterning</title>
      <link>https://futureofficelabs.com/blog/the-douat-for-patterning.html</link>
      <guid isPermaLink="true">https://futureofficelabs.com/blog/the-douat-for-patterning.html</guid>
      <pubDate>Thu, 16 Oct 2025 00:00:00 +0000</pubDate>
      <description>&lt;p&gt;I'm still resonating from the awesome &lt;a href="https://2025.algorithmicpattern.org/sheffield/"&gt;Alpaca Festival&lt;/a&gt; I attended a few weeks ago. There were many stimulating ideas there. Not least another &lt;a href="https://2025.algorithmicpattern.org/proceedings/QBK79E/paper.html"&gt;presentation&lt;/a&gt; by Rocco Lorenzo Modugno and his colleagues. Building on an &lt;a href="https://alpaca.pubpub.org/pub/b0ikfd74/release/1"&gt;earlier tak&lt;/a&gt; on the tiling pattern notation of Dominique Douat which uses the letters A,B,C and D to represent 4 orientations of a square tile, divided diagonally into black and white triangles. You can build many complex patterns out of such triangles simply by playing with their orientation.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://futureofficelabs.com/assets/blog/the-douat-for-patterning/1760545802157.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Anyway, inspired by this, I decided to implement something similar in my Patterning library. A simple notation, or "Domain Specific Language" for the orientation of tiles.&lt;/p&gt;
&lt;p&gt;But as Patterning is all about composition of smaller patterns into larger ones, I made A,B,C and D represent not simply rotated tiles, but functions to rotate any pattern in one of the four orientations. I then invented a structure I called the Douat.&lt;/p&gt;
&lt;p&gt;A Douat is slightly strange and convoluted to explain. But very easy to work with.&lt;/p&gt;
&lt;p&gt;Essentially a Douat object starts with a pattern inside it. And we pass it through a series of these "Douatic" transformation functions. Initially it starts taking those transformations and storing copies of its pattern transformed by them. Then when it has collected 4 of these transformed patterns it turns itself into a larger pattern with one of each of these transformed versions of the pattern in each quadrant. If we continue to pass it through the Douatic functions we will be able to play the same game with this first compound.&lt;/p&gt;
&lt;p&gt;Let's step through.&lt;/p&gt;
&lt;p&gt;If we pass the Douat through the following &lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;    A A A A&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we will get the same tile repeated in each quadrant.&lt;/p&gt;
&lt;p&gt;&lt;img alt="d1" src="https://futureofficelabs.com/assets/blog/the-douat-for-patterning/d1.png"&gt;&lt;/p&gt;
&lt;p&gt;On the other hand, if we pass it through &lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;    A B C D&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we get different orientations in each quadrant  &lt;/p&gt;
&lt;p&gt;&lt;img alt="d2" src="https://futureofficelabs.com/assets/blog/the-douat-for-patterning/d2.png"&gt;&lt;/p&gt;
&lt;p&gt;If we move to a second row, &lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;A B C D
A A A A&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We get that initial compound of four tiles now repeated itself 4 times.&lt;/p&gt;
&lt;p&gt;&lt;img alt="d3" src="https://futureofficelabs.com/assets/blog/the-douat-for-patterning/d3.png"&gt;&lt;/p&gt;
&lt;p&gt;In practice that original compounded pattern made with A B C D has a rotational symmetry so it doesn't matter what orientation we make it. But suppose we make the first level less rotationally symmetric, as in &lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;A B D C&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img alt="d4" src="https://futureofficelabs.com/assets/blog/the-douat-for-patterning/d4.png"&gt;&lt;/p&gt;
&lt;p&gt;Then &lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;A B D C
A A A A&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;will look like &lt;/p&gt;
&lt;p&gt;&lt;img alt="d5" src="https://futureofficelabs.com/assets/blog/the-douat-for-patterning/d5.png"&gt;&lt;/p&gt;
&lt;p&gt;But &lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;A B D C
A B C D&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;will look like &lt;/p&gt;
&lt;p&gt;&lt;img alt="d6" src="https://futureofficelabs.com/assets/blog/the-douat-for-patterning/d6.png"&gt;&lt;/p&gt;
&lt;p&gt;And we can keep going as long as we like.&lt;/p&gt;
&lt;h3 id="reflections"&gt;Reflections&lt;/h3&gt;
&lt;p&gt;We can supplement the rotations with reflections : horizontal, vertical and both. These are defined as the functions E, G and F respectively. This gives us a wider vocabulary of transformations to play with. &lt;/p&gt;
&lt;p&gt;We can start with a simple &lt;code&gt;A E G F&lt;/code&gt; combination. As in&lt;/p&gt;
&lt;p&gt;&lt;img alt="d7" src="https://futureofficelabs.com/assets/blog/the-douat-for-patterning/d7.png"&gt;&lt;/p&gt;
&lt;p&gt;and build it into complex patterns like &lt;/p&gt;
&lt;p&gt;&lt;img alt="d8" src="https://futureofficelabs.com/assets/blog/the-douat-for-patterning/d8.png"&gt;&lt;/p&gt;
&lt;p&gt;with&lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;   A E G F
   A B C D
   A B F B
   C F C D&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first pattern on this page is made with :&lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;    A E E A
    A B C D
    A E G F
    A A A A&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because this is Patterning, we aren't restricted to just the Truchet triangle tiles. Any pattern can work. So we can start with any simple tile and build it into something beautiful.&lt;/p&gt;
&lt;p&gt;&lt;img alt="d9" src="https://futureofficelabs.com/assets/blog/the-douat-for-patterning/untitled.png"&gt;&lt;/p&gt;
&lt;h3 id="how-the-douat-works"&gt;How The Douat Works&lt;/h3&gt;
&lt;p&gt;Here's the fairly short code implementation of the Douat itself. &lt;/p&gt;
&lt;pre class="code" data-lang="clojure"&gt;&lt;code class="language-clojure"&gt;
&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;defn&lt;/span&gt; &lt;span class="tok-variable"&gt;Douat&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-variable"&gt;p&lt;/span&gt;&lt;span class="tok-punct"&gt;]&lt;/span&gt; 
  &lt;span class="tok-punct"&gt;{&lt;/span&gt;&lt;span class="tok-string"&gt;:p&lt;/span&gt; &lt;span class="tok-variable"&gt;p&lt;/span&gt;
   &lt;span class="tok-string"&gt;:ps&lt;/span&gt; &lt;span class="tok-punct"&gt;[]&lt;/span&gt;
   &lt;span class="tok-string"&gt;:depth&lt;/span&gt; &lt;span class="tok-number"&gt;0&lt;/span&gt;&lt;span class="tok-punct"&gt;}&lt;/span&gt;
  &lt;span class="tok-punct"&gt;)&lt;/span&gt;


&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;defn&lt;/span&gt; &lt;span class="tok-variable"&gt;add-douat&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-variable"&gt;f&lt;/span&gt;&lt;span class="tok-punct"&gt;]&lt;/span&gt;
  &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;fn&lt;/span&gt; &lt;span class="tok-punct"&gt;[{&lt;/span&gt;&lt;span class="tok-string"&gt;:keys&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-variable"&gt;p&lt;/span&gt; &lt;span class="tok-variable"&gt;ps&lt;/span&gt; &lt;span class="tok-variable"&gt;depth&lt;/span&gt;&lt;span class="tok-punct"&gt;]}]&lt;/span&gt;
    &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;if&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;&amp;lt;&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;count&lt;/span&gt; &lt;span class="tok-variable"&gt;ps&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; &lt;span class="tok-number"&gt;3&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt;
      &lt;span class="tok-punct"&gt;{&lt;/span&gt;&lt;span class="tok-string"&gt;:p&lt;/span&gt; &lt;span class="tok-variable"&gt;p&lt;/span&gt;
       &lt;span class="tok-string"&gt;:ps&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;concat&lt;/span&gt; &lt;span class="tok-variable"&gt;ps&lt;/span&gt; &lt;span class="tok-punct"&gt;[(&lt;/span&gt;&lt;span class="tok-function"&gt;f&lt;/span&gt; &lt;span class="tok-variable"&gt;p&lt;/span&gt;&lt;span class="tok-punct"&gt;)])&lt;/span&gt;
       &lt;span class="tok-string"&gt;:depth&lt;/span&gt; &lt;span class="tok-variable"&gt;depth&lt;/span&gt;
       &lt;span class="tok-punct"&gt;}&lt;/span&gt;
      &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;let&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-variable"&gt;q&lt;/span&gt;
            &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;stack&lt;/span&gt;
             &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;nth&lt;/span&gt; &lt;span class="tok-variable"&gt;ps&lt;/span&gt; &lt;span class="tok-number"&gt;0&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;scale&lt;/span&gt; &lt;span class="tok-number"&gt;0.5&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;translate&lt;/span&gt; &lt;span class="tok-number"&gt;-0.5&lt;/span&gt; &lt;span class="tok-number"&gt;-0.5&lt;/span&gt;&lt;span class="tok-punct"&gt;))&lt;/span&gt;
             &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;nth&lt;/span&gt; &lt;span class="tok-variable"&gt;ps&lt;/span&gt; &lt;span class="tok-number"&gt;1&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;scale&lt;/span&gt; &lt;span class="tok-number"&gt;0.5&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;translate&lt;/span&gt; &lt;span class="tok-number"&gt;0.5&lt;/span&gt; &lt;span class="tok-number"&gt;-0.5&lt;/span&gt;&lt;span class="tok-punct"&gt;))&lt;/span&gt;
             &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;nth&lt;/span&gt; &lt;span class="tok-variable"&gt;ps&lt;/span&gt; &lt;span class="tok-number"&gt;2&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;scale&lt;/span&gt; &lt;span class="tok-number"&gt;0.5&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;translate&lt;/span&gt; &lt;span class="tok-number"&gt;0.5&lt;/span&gt; &lt;span class="tok-number"&gt;0.5&lt;/span&gt;&lt;span class="tok-punct"&gt;))&lt;/span&gt;
             &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;f&lt;/span&gt; &lt;span class="tok-variable"&gt;p&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;scale&lt;/span&gt; &lt;span class="tok-number"&gt;0.5&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;translate&lt;/span&gt; &lt;span class="tok-number"&gt;-0.5&lt;/span&gt; &lt;span class="tok-number"&gt;0.5&lt;/span&gt;&lt;span class="tok-punct"&gt;))&lt;/span&gt;
             &lt;span class="tok-punct"&gt;)]&lt;/span&gt;
        &lt;span class="tok-punct"&gt;{&lt;/span&gt;&lt;span class="tok-string"&gt;:ps&lt;/span&gt; &lt;span class="tok-punct"&gt;[]&lt;/span&gt;
         &lt;span class="tok-string"&gt;:p&lt;/span&gt; &lt;span class="tok-variable"&gt;q&lt;/span&gt;
         &lt;span class="tok-string"&gt;:depth&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;+&lt;/span&gt; &lt;span class="tok-number"&gt;1&lt;/span&gt; &lt;span class="tok-variable"&gt;depth&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt;
         &lt;span class="tok-punct"&gt;}))))&lt;/span&gt;

&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-variable"&gt;A&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;add-douat&lt;/span&gt; &lt;span class="tok-variable"&gt;identity&lt;/span&gt;&lt;span class="tok-punct"&gt;))&lt;/span&gt;
&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-variable"&gt;B&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;add-douat&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;fn&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-variable"&gt;p&lt;/span&gt;&lt;span class="tok-punct"&gt;]&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;rotate&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;/&lt;/span&gt; &lt;span class="tok-variable"&gt;PI&lt;/span&gt; &lt;span class="tok-number"&gt;2&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; &lt;span class="tok-variable"&gt;p&lt;/span&gt;&lt;span class="tok-punct"&gt;))))&lt;/span&gt;
&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-variable"&gt;C&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;add-douat&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;fn&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-variable"&gt;p&lt;/span&gt;&lt;span class="tok-punct"&gt;]&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;rotate&lt;/span&gt; &lt;span class="tok-variable"&gt;PI&lt;/span&gt; &lt;span class="tok-variable"&gt;p&lt;/span&gt;&lt;span class="tok-punct"&gt;))))&lt;/span&gt;
&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-variable"&gt;D&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;add-douat&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;fn&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-variable"&gt;p&lt;/span&gt;&lt;span class="tok-punct"&gt;]&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;rotate&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;*&lt;/span&gt; &lt;span class="tok-number"&gt;3&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;/&lt;/span&gt; &lt;span class="tok-variable"&gt;PI&lt;/span&gt; &lt;span class="tok-number"&gt;2&lt;/span&gt;&lt;span class="tok-punct"&gt;))&lt;/span&gt; &lt;span class="tok-variable"&gt;p&lt;/span&gt;&lt;span class="tok-punct"&gt;))))&lt;/span&gt;
&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-variable"&gt;E&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;add-douat&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;fn&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-variable"&gt;p&lt;/span&gt;&lt;span class="tok-punct"&gt;]&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;h-reflect&lt;/span&gt; &lt;span class="tok-variable"&gt;p&lt;/span&gt;&lt;span class="tok-punct"&gt;))))&lt;/span&gt;
&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-variable"&gt;F&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;add-douat&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;fn&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-variable"&gt;p&lt;/span&gt;&lt;span class="tok-punct"&gt;]&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;v-reflect&lt;/span&gt; &lt;span class="tok-variable"&gt;p&lt;/span&gt;&lt;span class="tok-punct"&gt;))))&lt;/span&gt;
&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-variable"&gt;G&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;add-douat&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;fn&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-variable"&gt;p&lt;/span&gt;&lt;span class="tok-punct"&gt;]&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="tok-variable"&gt;p&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;h-reflect&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;v-reflect&lt;/span&gt;&lt;span class="tok-punct"&gt;)))))&lt;/span&gt;
&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;defn&lt;/span&gt; &lt;span class="tok-variable"&gt;R&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-variable"&gt;douat&lt;/span&gt;&lt;span class="tok-punct"&gt;]&lt;/span&gt;
  &lt;span class="tok-punct"&gt;((&lt;/span&gt;&lt;span class="tok-function"&gt;rand-nth&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-variable"&gt;A&lt;/span&gt; &lt;span class="tok-variable"&gt;B&lt;/span&gt; &lt;span class="tok-variable"&gt;C&lt;/span&gt; &lt;span class="tok-variable"&gt;D&lt;/span&gt;&lt;span class="tok-punct"&gt;])&lt;/span&gt; &lt;span class="tok-variable"&gt;douat&lt;/span&gt;&lt;span class="tok-punct"&gt;))&lt;/span&gt;
&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;defn&lt;/span&gt; &lt;span class="tok-variable"&gt;Q&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-variable"&gt;douat&lt;/span&gt;&lt;span class="tok-punct"&gt;]&lt;/span&gt;
  &lt;span class="tok-punct"&gt;((&lt;/span&gt;&lt;span class="tok-function"&gt;rand-nth&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-variable"&gt;A&lt;/span&gt; &lt;span class="tok-variable"&gt;B&lt;/span&gt; &lt;span class="tok-variable"&gt;C&lt;/span&gt; &lt;span class="tok-variable"&gt;D&lt;/span&gt; &lt;span class="tok-variable"&gt;E&lt;/span&gt; &lt;span class="tok-variable"&gt;F&lt;/span&gt; &lt;span class="tok-variable"&gt;G&lt;/span&gt;&lt;span class="tok-punct"&gt;])&lt;/span&gt; &lt;span class="tok-variable"&gt;douat&lt;/span&gt;&lt;span class="tok-punct"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The implementation consists of defining a Douat itself. It's a simple map or dictionary with three fields : &lt;code&gt;:p&lt;/code&gt; is the current pattern, &lt;code&gt;:ps&lt;/code&gt; is the list of the next collection of patterns we are building up. And &lt;code&gt;:depth&lt;/code&gt; is a depth tracker. The &lt;code&gt;Douat&lt;/code&gt; function creates that map from an initial pattern.&lt;/p&gt;
&lt;p&gt;The functionality of the Douat is encapsulated in the function &lt;code&gt;add-douat&lt;/code&gt; which is not intended to be used directly but which is a higher-order function that adapts any function that transforms a normal pattern, into a function which works on a Douat-wrapped version of the pattern.&lt;/p&gt;
&lt;p&gt;This allows us to use it in the Douat world. Is it like a "monad"? It not really a monad, but it's kinda that way of thinking. We want to make a bunch of functions composable into a pipeline where they all receive and return Douats.&lt;/p&gt;
&lt;p&gt;So when we call a Douatic function on a Douat it does one of two things.&lt;/p&gt;
&lt;p&gt;If we haven't yet collected four new pieces of pattern, it just applies the wrapped transformation to the Douat's current pattern, and appends it to the list that's building up at the &lt;code&gt;:ps&lt;/code&gt; key. And returns the Douat&lt;/p&gt;
&lt;p&gt;If it is passing through the fourth Douatic function in the sequence, it now creates a new pattern with one of the transformations of the old pattern in each quadrant and makes this the new pattern at &lt;code&gt;:p&lt;/code&gt;. And then it wipes the &lt;code&gt;:ps&lt;/code&gt; list to start collecting four more transformations.&lt;/p&gt;
&lt;p&gt;So as the Douat passes through a pipeline of Douatic functions it collects them into groups of four, using each to make one quadrant of a new pattern. And this is indefinitely recursive, to build up increasingly complex patterns of more and more tiles.&lt;/p&gt;
&lt;p&gt;The functions we currently create are the orginal A, B, C and D inspired by Douat's notation. We then extend this with E, F and G which are reflections (as opposed to rotations). And R and Q which randomly choose one of the others. &lt;/p&gt;
&lt;p&gt;Why do we use such short cryptic names? Because this lets us write code like this :&lt;/p&gt;
&lt;pre class="code" data-lang="clojure"&gt;&lt;code class="language-clojure"&gt;
&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-variable"&gt;my-tile&lt;/span&gt;
  &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;stack&lt;/span&gt;
    &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;square&lt;/span&gt; &lt;span class="tok-punct"&gt;{&lt;/span&gt;&lt;span class="tok-string"&gt;:fill&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;p-color&lt;/span&gt; &lt;span class="tok-number"&gt;90&lt;/span&gt; &lt;span class="tok-number"&gt;0&lt;/span&gt; &lt;span class="tok-number"&gt;0&lt;/span&gt;&lt;span class="tok-punct"&gt;)})&lt;/span&gt;
    &lt;span class="tok-punct"&gt;[(&lt;/span&gt;&lt;span class="tok-function"&gt;-&amp;gt;SShape&lt;/span&gt;
       &lt;span class="tok-punct"&gt;{&lt;/span&gt;&lt;span class="tok-string"&gt;:fill&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;p-color&lt;/span&gt; &lt;span class="tok-number"&gt;0&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; 
        &lt;span class="tok-string"&gt;:stroke-weight&lt;/span&gt; &lt;span class="tok-number"&gt;1&lt;/span&gt;
        &lt;span class="tok-string"&gt;:stroke&lt;/span&gt; &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-function"&gt;p-color&lt;/span&gt; &lt;span class="tok-number"&gt;255&lt;/span&gt; &lt;span class="tok-number"&gt;255&lt;/span&gt; &lt;span class="tok-number"&gt;0&lt;/span&gt;&lt;span class="tok-punct"&gt;)}&lt;/span&gt;
       &lt;span class="tok-punct"&gt;[[&lt;/span&gt;&lt;span class="tok-number"&gt;1&lt;/span&gt; &lt;span class="tok-number"&gt;1&lt;/span&gt;&lt;span class="tok-punct"&gt;]&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-number"&gt;-1&lt;/span&gt; &lt;span class="tok-number"&gt;1&lt;/span&gt;&lt;span class="tok-punct"&gt;]&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-number"&gt;-1&lt;/span&gt; &lt;span class="tok-number"&gt;-1&lt;/span&gt;&lt;span class="tok-punct"&gt;]&lt;/span&gt; &lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-number"&gt;1&lt;/span&gt; &lt;span class="tok-number"&gt;0&lt;/span&gt;&lt;span class="tok-punct"&gt;]])]))&lt;/span&gt;

&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="tok-variable"&gt;my-tile&lt;/span&gt;

   &lt;span class="tok-variable"&gt;Douat&lt;/span&gt;

   &lt;span class="tok-variable"&gt;A&lt;/span&gt; &lt;span class="tok-variable"&gt;E&lt;/span&gt; &lt;span class="tok-variable"&gt;E&lt;/span&gt; &lt;span class="tok-variable"&gt;A&lt;/span&gt;
   &lt;span class="tok-variable"&gt;A&lt;/span&gt; &lt;span class="tok-variable"&gt;B&lt;/span&gt; &lt;span class="tok-variable"&gt;C&lt;/span&gt; &lt;span class="tok-variable"&gt;D&lt;/span&gt;
   &lt;span class="tok-variable"&gt;A&lt;/span&gt; &lt;span class="tok-variable"&gt;E&lt;/span&gt; &lt;span class="tok-variable"&gt;E&lt;/span&gt; &lt;span class="tok-variable"&gt;A&lt;/span&gt;
   &lt;span class="tok-variable"&gt;B&lt;/span&gt; &lt;span class="tok-variable"&gt;E&lt;/span&gt; &lt;span class="tok-variable"&gt;E&lt;/span&gt; &lt;span class="tok-variable"&gt;B&lt;/span&gt;

  &lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-string"&gt;:p&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt;
  &lt;span class="tok-punct"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we create the initial tile with standard Patterning functions. &lt;/p&gt;
&lt;p&gt;Then we use the threading macro to make the simple pipeline that will send the pattern first through the Douat function to wrap it as a Douat. Then through a series of Douatic functions. Because these are single letter names we can just group them in lines of 4 to make them intelligible.&lt;/p&gt;
&lt;p&gt;Finally we want to extract the end pattern from the Douat wrapper, this is what the &lt;code&gt;(:p)&lt;/code&gt; does.&lt;/p&gt;
&lt;p&gt;In practice we could wrap any of Patterning's functions that transform patterns to work in this Douatic way. &lt;/p&gt;
&lt;p&gt;You can play with the Douat within the &lt;a href="https://alchemyislands.com/assets/patterning-tutorial/workbench/index.html?code=(-%3E%0A%20%20(Douat%20%0A%20%20%20(stack%0A%20%20%20%20(square%20%7B%3Afill%20(p-color%2090%200%200)%7D)%0A%20%20%20%20%5B(-%3ESShape%0A%20%20%20%20%20%20%20%7B%3Afill%20(p-color%200)%20%0A%20%20%20%20%20%20%20%20%3Astroke-weight%201%0A%20%20%20%20%20%20%20%20%3Astroke%20(p-color%20255%20255%200)%7D%0A%20%20%20%20%20%20%20%5B%5B1%201%5D%20%5B-1%201%5D%20%5B-1%20-1%5D%20%5B1%200%5D%5D)%5D%0A%20%20%20%20))%0A%20%0A%20%20%20A%20E%20E%20A%0A%20%20%20A%20B%20C%20D%0A%20%20%20A%20E%20E%20A%0A%20%20%20B%20E%20E%20B%0A%20%20(%3Ap)%0A%0A%20%20)"&gt;Patterning Workbench&lt;/a&gt; on my Alchemy Islands site.&lt;/p&gt;
&lt;p&gt;The nice thing about the Douat is not just that it gives us a very powerful and concise way to make a lot of quite beautiful patterns. But that the implementation turns out to be so simple and elegant. Inventing it has taught and inspired me a lot about the power of functional thinking. You can imagine building up many other expressive DSLs this way in Clojure. The fluent "MailQuery" library I wrote in Python recently has some similarities. But this one I really find cool.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Keyboard DAG Editing</title>
      <link>https://futureofficelabs.com/blog/keyboard-dag-editing.html</link>
      <guid isPermaLink="true">https://futureofficelabs.com/blog/keyboard-dag-editing.html</guid>
      <pubDate>Wed, 15 Oct 2025 00:00:00 +0000</pubDate>
      <description>&lt;p&gt;Directed Acyclic Graphs or DAGs are an important data-structure for representing any kind of processing pipeline.&lt;/p&gt;
&lt;p&gt;They turn up in audio applications, from modular synthesisers to Pure Data patches. (As long as there are no feedback loops.) They are used in Machine Learning pipelines. In continuous integration and delivery of software and other business processes.&lt;/p&gt;
&lt;p&gt;Something that I've long thought about are different ideas for editing DAGs. Typically we use custom editors highly coupled to a specific application. Mostly these are variants of drawing boxes and arrows. Or "node based" editors.&lt;/p&gt;
&lt;p&gt;But I'm always fascinated by the idea of generic editors for particular data-structures. When we decouple the editor and the structure from the particular application, we unlock a lot of potential value as we can then quickly think of applying the structure in new contexts.&lt;/p&gt;
&lt;p&gt;We have these for text. (Most code editors handle multiple programming languages. Even word-processors aren't specific to "novels" vs "business letters".) And image editing. And spreadsheets for grid or table-shaped data. But rarely for other data shapes.&lt;/p&gt;
&lt;p&gt;I've played around with writing my own generic boxes and arrows type editors. But one thing that I don't particularly like is having to use the mouse. It's not that I dislike the mouse in itself. It's that anything that forces a lot of switching between mouse and keyboard gets frustrating. And in my experience, much data-flow DAG editing involves a LOT of backwards and forwards between the mouse and keyboard.&lt;/p&gt;
&lt;p&gt;Wouldn't it be great if we could have a simple, purely keyboard way of editing DAGs?&lt;/p&gt;
&lt;p&gt;Well DAGs often DO have text representations. Graphviz is the popular open-source library for visualising DAGs and is based on the DOT text format. Pure Data has its own text-based file format.&lt;/p&gt;
&lt;p&gt;But such text files aren't intended for humans to read or follow. They don't show the graph structure clearly. And editing them often requires you to remember arbitrary IDs for nodes. These formats are also tightly coupled to the specific application.&lt;/p&gt;
&lt;p&gt;I'd like something which &lt;em&gt;feels like&lt;/em&gt; a simple text editor, that is completely keyboard controlled, and which nevertheless lets me clearly see, understand and edit the DAG structure. &lt;/p&gt;
&lt;p&gt;So the other day, I had some thoughts and set down to vibe-code a concept demonstration.&lt;/p&gt;
&lt;p&gt;&lt;img alt="DAG Editor" src="https://futureofficelabs.com/assets/blog/keyboard-dag-editing/dageditor.png"&gt;&lt;/p&gt;
&lt;p&gt;The main "insight" is to borrow a multi-column paradigm from spreadsheets. Arrow keys are fine for navigating a 2D grid of cells. And while a DAG is NOT a grid, we can nevertheless overlay a DAG onto a grid relatively "efficiently". By which I mean, we only need as many columns as our maximum number of parallel paths through the graph. And we can often squash multiple paths into the same column as long as they are not overlapping.&lt;/p&gt;
&lt;p&gt;We treat a column in the grid a simple sequence of nodes. Each automatically connected from the one above. We use a specific key shortcut, ctrl and "+" to fork a second sequence. This moves into a new empty column. We also have a way merge a sequence back into another sequence. This starts by "grabbing the output" from a node, with ctrl+Return. And then pasting it onto another node with ctrl+v where it is treated as a second input.&lt;/p&gt;
&lt;p&gt;With these three extra key controls - fork, grab output and paste input; and navigation within the grid using Shift+arrow keys - we have the ability to build up our DAG with simple keyboard presses.&lt;/p&gt;
&lt;p&gt;The final requirement is to delete nodes and connections. Ctrl-backspace deletes a node. While deleting individual connections requires a pop-up "palette" opened with ctrl+"." We can navigate within this palette with arrow keys and delete unwanted connections with the backspace key.&lt;/p&gt;
&lt;p&gt;So this is just a first experiment. It's buggy and somewhat klunky but you can play with it and get a feel for using it here : &lt;a href="https://futureofficelabs.com/apps/dageditor/"&gt;DAGEdit&lt;/a&gt;. When the code is in better shape and some of the more egregious bugs are ironed out, I'll make it available.&lt;/p&gt;</description>
    </item>
    <item>
      <title>MailQuery</title>
      <link>https://futureofficelabs.com/blog/first-post.html</link>
      <guid isPermaLink="true">https://futureofficelabs.com/blog/first-post.html</guid>
      <pubDate>Tue, 12 Aug 2025 00:00:00 +0000</pubDate>
      <description>&lt;p&gt;I admit, I like jQuery. Not jQuery as a full web UI framework, but the notion of a simple, "fluent" interface for accessing something complicated like the DOM, that jQuery introduced me to.&lt;/p&gt;
&lt;p&gt;Every now and then I need to do something with another complicated thing and a light-bulb goes off in my head and I think "I wish I had an xQuery for that."&lt;/p&gt;
&lt;p&gt;I originally did it when I got bored with crawling around my file-system the old-fashioned way. And wrote https://github.com/interstar/FSQuery&lt;/p&gt;
&lt;p&gt;Meanwhile, I've always hated Gmail filters. And increasingly I've been thinking about whether AI might finally help to get my aburdly cluttered Gmail inbox under control.&lt;/p&gt;
&lt;p&gt;But then I realised I just wanted a better way to talk to my mailbox from Python. So I've been vibe-coding for a few days, and here's the result, a Python library called "mailquery" : &lt;a href="https://github.com/interstar/mailquery"&gt;https://github.com/interstar/mailquery&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here's how I can currently manage my Gmail account, in the REPL with a fluent interface and FP attitude.&lt;/p&gt;
&lt;p&gt;Import from the library&lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;    &lt;span class="tok-keyword"&gt;from&lt;/span&gt; &lt;span class="tok-type"&gt;mailquery&lt;/span&gt; &lt;span class="tok-keyword"&gt;import&lt;/span&gt; GmailClient&lt;span class="tok-punct"&gt;,&lt;/span&gt; Mailbox&lt;span class="tok-punct"&gt;,&lt;/span&gt; SQLiteStorage&lt;span class="tok-punct"&gt;,&lt;/span&gt; OR&lt;span class="tok-punct"&gt;,&lt;/span&gt; spit&lt;span class="tok-punct"&gt;,&lt;/span&gt; HTMLPageBuilder&lt;span class="tok-punct"&gt;,&lt;/span&gt; SenderCollector&lt;span class="tok-punct"&gt;,&lt;/span&gt; EmailStatistics&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create the gmail client. The credentials are in "credentials.json". Note the library also has a plain IMAP client&lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;    client &lt;span class="tok-operator"&gt;=&lt;/span&gt; GmailClient&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-string"&gt;"credentials.json"&lt;/span&gt;&lt;span class="tok-punct"&gt;,&lt;/span&gt;allow_delete&lt;span class="tok-operator"&gt;=&lt;/span&gt;&lt;span class="tok-constant"&gt;True&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create mailbox from the client. Filter for emails later than 1st August, 2025 that are from either one of hello@restofworld.org or ai.plus@axios. &lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;    news &lt;span class="tok-operator"&gt;=&lt;/span&gt; Mailbox&lt;span class="tok-punct"&gt;(&lt;/span&gt;client&lt;span class="tok-punct"&gt;)&lt;/span&gt;&lt;span class="tok-operator"&gt;.&lt;/span&gt;after&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-string"&gt;"2025-08-01"&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt;&lt;span class="tok-operator"&gt;.&lt;/span&gt;from_&lt;span class="tok-punct"&gt;(&lt;/span&gt;OR&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-string"&gt;"hello@restofworld.org"&lt;/span&gt;&lt;span class="tok-punct"&gt;,&lt;/span&gt;&lt;span class="tok-string"&gt;"ai.plus@axios.com"&lt;/span&gt;&lt;span class="tok-punct"&gt;,))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note this is a "fluent" interface where you chain filters together with the dot operator. Each filter call adds itself to the Mailbox and returns the Mailbox. There are standard built in ones like from_, to, before, after, subject_contains etc. You can add as many as you like.&lt;/p&gt;
&lt;p&gt;OR is a special class that represents any of the arguments. from_(OR("hello@restofworld.org","ai.plus@axios.com")) matches emails from either of these two addresses. The OR can handle as many values as you give it.&lt;/p&gt;
&lt;p&gt;There are also the two generic filters : &lt;code&gt;include_when()&lt;/code&gt; and &lt;code&gt;exclude_when()&lt;/code&gt; These take "predicate" functions. That is, functions which map from an email object to a True/False value&lt;/p&gt;
&lt;p&gt;Make sure you understand this concept clearly. "include_when()" means if the predicate returns True, then this filter allows the email through to the next stage "exclude_when()" means that if the predicate returns True, the mail is blocked from passing through to the next stage. But will pass through if the predicate returns False&lt;/p&gt;
&lt;p&gt;Anyway, after the last command, "news" now contains a mailbox with the after and from_ filters attached. &lt;/p&gt;
&lt;p&gt;But note that nothing has actually run yet. MailQuery is a pretty lazy library.&lt;/p&gt;
&lt;p&gt;Let's now just list the mails that match. verbose=False prints just the basic header, verbose=True would print a bunch of extra warnings and diagnostics.&lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;    news&lt;span class="tok-operator"&gt;.&lt;/span&gt;list_all&lt;span class="tok-punct"&gt;(&lt;/span&gt;verbose&lt;span class="tok-operator"&gt;=&lt;/span&gt;&lt;span class="tok-constant"&gt;False&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This has now triggered the query to execute. It ran through the mailbox looking for emails that matched the filters. &lt;/p&gt;
&lt;p&gt;Note that all the filtering is done on the client. Which means downloading all the email headers to test them against the criteria 
BUT as an optimisation for Gmail, both the after() filter and the from_() filter get turned into Gmail server-side filters by our GmailClient class. This makes things MUCH faster, but is completely transparent to the user.  You don't need to know anything about Gmail to get this benefit. It's entirely handled by the GmailClient class we used when we created the Mailbox object.&lt;/p&gt;
&lt;p&gt;If you aren't using Gmail, or a server where we can use such optmisations, then MailQuery will just chug along, slowly, downloading all the mail 
and filtering it locally. It's much slower, but it works identically&lt;/p&gt;
&lt;p&gt;Remember I said MailQuery was lazy?&lt;/p&gt;
&lt;p&gt;At this point the mail headers have been downloaded and cached. If we run list_all() again, we'll just pull them out of the local cache.&lt;/p&gt;
&lt;p&gt;However the bodies of the emails are not yet downloaded. And won't be until something calls a get body or get html type function on them.
&lt;code&gt;body_contains()&lt;/code&gt;, a test whether a string is in the body of the email, is one such filter. If included in the filter chain it will force a download of the email body. &lt;/p&gt;
&lt;p&gt;Once bodies are downloaded, the Email objects in the mailbox will cache that too.&lt;/p&gt;
&lt;p&gt;Perhaps we want to store the emails that matched our criteria. A Storage object can do that. And we place a store_local() end point on our filter chain.&lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;    storage &lt;span class="tok-operator"&gt;=&lt;/span&gt; SQLiteStorage&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-string"&gt;"newsletters.db"&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt;
    news&lt;span class="tok-operator"&gt;.&lt;/span&gt;store_local&lt;span class="tok-punct"&gt;(&lt;/span&gt;storage&lt;span class="tok-punct"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that's it, the emails are stored in the database. We can now delete them from the gmail account&lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;    news&lt;span class="tok-operator"&gt;.&lt;/span&gt;delete&lt;span class="tok-punct"&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But wait, there's more! These newsletters have all their useful stuff in an HTML attachment field. That's a pain. We can extract the text from the html with Beautiful Soup. But we don't really want to hardwire Beautiful Soup into our mail library do we?&lt;/p&gt;
&lt;p&gt;We would like a way to optionally process a field in the mail record into some further information. &lt;/p&gt;
&lt;p&gt;We also want to store this extra information in the email.&lt;/p&gt;
&lt;p&gt;Fortunately, MailQuery has a mechanism for that : &lt;code&gt;add_attribute(att_name,fn)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;add_attribute()&lt;/code&gt; works like a filter. But it always returns True. It never removes anything from the mailbox.&lt;/p&gt;
&lt;p&gt;What it DOES do is transform the email in some way. The function &lt;code&gt;fn&lt;/code&gt; needs to be a function that maps emails to some other value. &lt;code&gt;att_name&lt;/code&gt; is a string . The &lt;code&gt;add_attribute&lt;/code&gt; filter calls &lt;code&gt;fn&lt;/code&gt; on each mail passing through, and stores the result in an extra attribute under the name `att_name&lt;/p&gt;
&lt;p&gt;For example, we can use Beautiful Soup to extract text from the html like this.&lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;&lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-function"&gt;extract_html_text&lt;/span&gt;&lt;span class="tok-punct"&gt;(&lt;/span&gt;email&lt;span class="tok-punct"&gt;):&lt;/span&gt;
    &lt;span class="tok-keyword"&gt;try&lt;/span&gt;&lt;span class="tok-punct"&gt;:&lt;/span&gt;
        html_content &lt;span class="tok-operator"&gt;=&lt;/span&gt; email&lt;span class="tok-operator"&gt;.&lt;/span&gt;get_html&lt;span class="tok-punct"&gt;()&lt;/span&gt;
        &lt;span class="tok-keyword"&gt;if&lt;/span&gt; html_content&lt;span class="tok-punct"&gt;:&lt;/span&gt;
            soup &lt;span class="tok-operator"&gt;=&lt;/span&gt; BeautifulSoup&lt;span class="tok-punct"&gt;(&lt;/span&gt;html_content&lt;span class="tok-punct"&gt;,&lt;/span&gt; &lt;span class="tok-string"&gt;'html.parser'&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt;
            &lt;span class="tok-keyword"&gt;return&lt;/span&gt; soup&lt;span class="tok-operator"&gt;.&lt;/span&gt;get_text&lt;span class="tok-punct"&gt;(&lt;/span&gt;separator&lt;span class="tok-operator"&gt;=&lt;/span&gt;&lt;span class="tok-string"&gt;'&lt;/span&gt; &lt;span class="tok-string"&gt;'&lt;/span&gt;&lt;span class="tok-punct"&gt;,&lt;/span&gt; strip&lt;span class="tok-operator"&gt;=&lt;/span&gt;&lt;span class="tok-constant"&gt;True&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt;
        &lt;span class="tok-keyword"&gt;return&lt;/span&gt; &lt;span class="tok-string"&gt;""&lt;/span&gt;
    &lt;span class="tok-keyword"&gt;except&lt;/span&gt; Exception &lt;span class="tok-keyword"&gt;as&lt;/span&gt; e&lt;span class="tok-punct"&gt;:&lt;/span&gt;
        &lt;span class="tok-builtin"&gt;print&lt;/span&gt;&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-string"&gt;f"Error extracting HTML text:&lt;/span&gt; &lt;span class="tok-string"&gt;{&lt;/span&gt;e&lt;span class="tok-string"&gt;}"&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt;
        &lt;span class="tok-keyword"&gt;return&lt;/span&gt; &lt;span class="tok-string"&gt;""&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can add this as a new attribute&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;news.add_attribute("html_as_text",extract_html_text).store_local(storage)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not only does the add_attribute add the extra attribute to all the emails. It also registers the extra attribute name in the Mailbox&lt;/p&gt;
&lt;p&gt;Then when we call store_local(storage), storage finds out about the extra attribute name and creates an extra column for it in the SQLite database&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But wait there's MORE ....&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We can filter. We can "map" mails to other values? Why not have trifecta and add some kind of fold or reduce?&lt;/p&gt;
&lt;p&gt;list_all(), delete() and store_local() are three functions we can add at the end of the pipeline to do something with our email&lt;/p&gt;
&lt;p&gt;But we also have a generic way to roll them up to get some kind of aggregate value out of them.&lt;/p&gt;
&lt;p&gt;Unsurprisingly it's called reduce_all(reducer)&lt;/p&gt;
&lt;p&gt;Here's what a Reducer has to look like&lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;&lt;span class="tok-keyword"&gt;class&lt;/span&gt; &lt;span class="tok-type"&gt;Reducer&lt;/span&gt; &lt;span class="tok-punct"&gt;:&lt;/span&gt;
    &lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-function"&gt;init_value&lt;/span&gt;&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;self&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; &lt;span class="tok-punct"&gt;:&lt;/span&gt; &lt;span class="tok-comment"&gt;# returns initial value&lt;/span&gt;
    &lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-function"&gt;fold&lt;/span&gt;&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;self&lt;/span&gt;&lt;span class="tok-punct"&gt;,&lt;/span&gt; &lt;span class="tok-builtin"&gt;next&lt;/span&gt;&lt;span class="tok-punct"&gt;:&lt;/span&gt;ParsedEmail&lt;span class="tok-punct"&gt;)&lt;/span&gt;  &lt;span class="tok-punct"&gt;:&lt;/span&gt; &lt;span class="tok-comment"&gt;# folds the next email into its internal accumulator&lt;/span&gt; 
    &lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-function"&gt;final&lt;/span&gt;&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;self&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; &lt;span class="tok-punct"&gt;:&lt;/span&gt; &lt;span class="tok-keyword"&gt;return&lt;/span&gt; the final value of the accumulator&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's use one to count the emails in our mailbox&lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;&lt;span class="tok-keyword"&gt;class&lt;/span&gt; &lt;span class="tok-type"&gt;Counter&lt;/span&gt;&lt;span class="tok-punct"&gt;(&lt;/span&gt;Reducer&lt;span class="tok-punct"&gt;):&lt;/span&gt;

    &lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-function"&gt;init_value&lt;/span&gt;&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;self&lt;/span&gt;&lt;span class="tok-punct"&gt;):&lt;/span&gt;
        &lt;span class="tok-builtin"&gt;self&lt;/span&gt;&lt;span class="tok-operator"&gt;.&lt;/span&gt;count &lt;span class="tok-operator"&gt;=&lt;/span&gt; &lt;span class="tok-number"&gt;0&lt;/span&gt;

    &lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-function"&gt;fold&lt;/span&gt;&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;self&lt;/span&gt;&lt;span class="tok-punct"&gt;,&lt;/span&gt; email&lt;span class="tok-punct"&gt;:&lt;/span&gt; ParsedEmail&lt;span class="tok-punct"&gt;):&lt;/span&gt;
        &lt;span class="tok-builtin"&gt;self&lt;/span&gt;&lt;span class="tok-operator"&gt;.&lt;/span&gt;count &lt;span class="tok-operator"&gt;+=&lt;/span&gt; &lt;span class="tok-number"&gt;1&lt;/span&gt;

    &lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-function"&gt;final&lt;/span&gt;&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;self&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; &lt;span class="tok-operator"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="tok-builtin"&gt;int&lt;/span&gt;&lt;span class="tok-punct"&gt;:&lt;/span&gt;
        &lt;span class="tok-keyword"&gt;return&lt;/span&gt; &lt;span class="tok-builtin"&gt;self&lt;/span&gt;&lt;span class="tok-operator"&gt;.&lt;/span&gt;count


mails&lt;span class="tok-operator"&gt;.&lt;/span&gt;reduce_all&lt;span class="tok-punct"&gt;(&lt;/span&gt;Counter&lt;span class="tok-punct"&gt;())&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Something more useful is to discover all the unique email addresses in a matching mail set&lt;/p&gt;
&lt;pre class="code" data-lang="python"&gt;&lt;code class="language-python"&gt;&lt;span class="tok-keyword"&gt;class&lt;/span&gt; &lt;span class="tok-type"&gt;SenderCollector&lt;/span&gt;&lt;span class="tok-punct"&gt;(&lt;/span&gt;Reducer&lt;span class="tok-punct"&gt;):&lt;/span&gt;
    &lt;span class="tok-string"&gt;"""Collect all unique sender email addresses"""&lt;/span&gt;

    &lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-function"&gt;init_value&lt;/span&gt;&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;self&lt;/span&gt;&lt;span class="tok-punct"&gt;):&lt;/span&gt;
        &lt;span class="tok-builtin"&gt;self&lt;/span&gt;&lt;span class="tok-operator"&gt;.&lt;/span&gt;senders&lt;span class="tok-punct"&gt;:&lt;/span&gt; Set&lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-builtin"&gt;str&lt;/span&gt;&lt;span class="tok-punct"&gt;]&lt;/span&gt; &lt;span class="tok-operator"&gt;=&lt;/span&gt; &lt;span class="tok-builtin"&gt;set&lt;/span&gt;&lt;span class="tok-punct"&gt;()&lt;/span&gt;

    &lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-function"&gt;fold&lt;/span&gt;&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;self&lt;/span&gt;&lt;span class="tok-punct"&gt;,&lt;/span&gt; email&lt;span class="tok-punct"&gt;:&lt;/span&gt; ParsedEmail&lt;span class="tok-punct"&gt;):&lt;/span&gt;
        &lt;span class="tok-builtin"&gt;self&lt;/span&gt;&lt;span class="tok-operator"&gt;.&lt;/span&gt;senders&lt;span class="tok-operator"&gt;.&lt;/span&gt;add&lt;span class="tok-punct"&gt;(&lt;/span&gt;email&lt;span class="tok-operator"&gt;.&lt;/span&gt;envelope&lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-string"&gt;'from'&lt;/span&gt;&lt;span class="tok-punct"&gt;])&lt;/span&gt;

    &lt;span class="tok-keyword"&gt;def&lt;/span&gt; &lt;span class="tok-function"&gt;final&lt;/span&gt;&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;self&lt;/span&gt;&lt;span class="tok-punct"&gt;)&lt;/span&gt; &lt;span class="tok-operator"&gt;-&amp;gt;&lt;/span&gt; List&lt;span class="tok-punct"&gt;[&lt;/span&gt;&lt;span class="tok-builtin"&gt;str&lt;/span&gt;&lt;span class="tok-punct"&gt;]:&lt;/span&gt;
        &lt;span class="tok-keyword"&gt;return&lt;/span&gt; &lt;span class="tok-builtin"&gt;list&lt;/span&gt;&lt;span class="tok-punct"&gt;(&lt;/span&gt;&lt;span class="tok-builtin"&gt;self&lt;/span&gt;&lt;span class="tok-operator"&gt;.&lt;/span&gt;senders&lt;span class="tok-punct"&gt;)&lt;/span&gt;

mails&lt;span class="tok-operator"&gt;.&lt;/span&gt;reduce_all&lt;span class="tok-punct"&gt;(&lt;/span&gt;SenderCollector&lt;span class="tok-punct"&gt;())&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope you see how this thing starts to stack up&lt;/p&gt;
&lt;p&gt;Apart from the delete() WHICH IS BLOODY DANGEROUS ... BE VERY CAREFUL WITH IT&lt;/p&gt;
&lt;p&gt;Apart from the delete() nothing else changes your mailbox. And everything gets cached locally&lt;/p&gt;
&lt;p&gt;So once you've run a mailbox filtering once, whether to print or store, runnning these further reducers will just iterate through mails in memory&lt;/p&gt;
&lt;p&gt;MailQuery is powerful for any automated email application. But my goal is to be able to use it in Python REPL to interact with my Gmail dynamically but programatically.&lt;/p&gt;
&lt;p&gt;One thing missing from all the automation above is a way to bring the human back into the loop. Sometimes even I can't think of a criterion for deleting or saving emails except by reading the mail and making a personal decision&lt;/p&gt;
&lt;p&gt;Never fear, we can bring the human back simply by invoking human()&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mails.subject_contains("important").human().delete()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;human() works like any other filter on the email chain. Except it turns the terminal into an interactive window where a human user can review and decide the fate of an email. The human can type "D" or SPACE. "D" is obviously intended to signal "delete this email". In fact, because human() is just another filter stage, what it ACTUALLY does is return a True, indicating that the email is to pass through to the next stage of the filter chain. SPACE on the other hand returns False, removing the email from the filter chain. These obviously only work as intended if the human() is slotted into a chain which ends at a delete(). You could obviously use the human() elsewhere, but you are likely to confuse yourself or your users. Again, MailQuery is a dangerous power-tool which will hurt you if you are not incredibly careful.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
