This Website Is Not A Tree


It must be emphasized, lest the orderly mind shrink in horror from anything that is not clearly articulated and categorized in tree form, that the idea of overlap, ambiguity, multiplicity of aspect and the semilattice are not less orderly than the rigid tree, but more so. They represent a thicker, tougher, more subtle and more complex view of structure.
Christopher Alexander, A City Is Not A Tree


Preliminaries

This is the first post of respatialized, a website about actual and potential spaces. Part of the reason it took me so long to launch it is because nearly every static site generator forces your writing into a tree-like structure. Only one lets you extend the site generation methods to reflect your own ideas: Matthew Butterick's

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 34, Columns 332-391
Source expression
(link "https://docs.racket-lang.org/pollen/"  "pollen")

. Because I want to combine the sequential and additive writing style of a blog with the associational and iterative nature of a wiki, this was the only choice.

It would have taken me even longer to get started if Joel Dueck hadn't already done the excellent work of creating

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 36, Columns 117-192
Source expression
(in-code (link "https://github.com/otherjoel/thenotepad" "thenotepad"))

, which includes functions to produce many of the things we expect from blogs, like sequential indices and RSS feeds, and many that we should expect, but don't (like the ability to generate a PDF from the blog). The code that generates this blog is forked from

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 36, Columns 454-519
Source expression
(link "https://github.com/otherjoel/thenotepad" "thenotepad")

and licensed under the MIT License.

Extensible textual notation

I recently switched from

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 41, Columns 26-48
Source expression
(in-code "pollen")

to

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 41, Columns 52-73
Source expression
(in-code "perun")

.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 41, Columns 75-96
Source expression
(in-code "perun")

's model of publishing everything via a composable collection of

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 41, Columns 161-181
Source expression
(in-code "boot")

tasks encapsulates everything that I want from

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 41, Columns 229-251
Source expression
(in-code "pollen")

's organizational and compositional capabilities.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 41, Columns 301-323
Source expression
(in-code "pollen")

's pagetrees can be recreated by mapping and filtering the sequential collections of

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 41, Columns 408-430
Source expression
(in-code "hiccup")

data structures

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 41, Columns 447-468
Source expression
(in-code "perun")

generates, and applying those transformations to generic collections comes more readily to me than creating

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 41, Columns 577-599
Source expression
(in-code ".ptree")

files (the Clojure refrain, it's just data, etc.). My artwork and other content is also written in and generated using Clojure, so I don't want to have to drop into a different language that I don't know as well just to get it out. For me, the ability to iterate quickly depends on low friction and the power of simplicity:

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 41, Columns 924-944
Source expression
(in-code "boot")

's Swiss army knife approach matches that perfectly.

However, I agree wholeheartedly with Matthew Butterick when

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 43, Columns 61-142
Source expression
(link "https://docs.racket-lang.org/pollen/second-tutorial.html" "he argues")

that Markdown is a constraining environment in which to write, especially if you're looking to write a sustained treatment of a topic which usually generates a deep and rich collection of self and cross references and its own conventions for referring back to subtopics organically over time. Markdown supports the lowest level of this: links. Anything else, you're on your own, but with a completely restricted method of manipulating the input texts.

Also, sometimes I want to contextually distinguish textual elements using CSS and I want to do it without rewriting my markdown parser. I currently do this by littering my markdown posts with

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 45, Columns 193-226
Source expression
(in-code "div class=\"...\"")

tags, which is kludgy and offers no way of systematically changing the classes applied to the textual element apart from doing find-replace on all of them with

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 45, Columns 387-407
Source expression
(in-code "grep")

.

The

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 47, Columns 5-33
Source expression
(in-code "#lang pollen")

directive provides a beautiful way of letting prose be prose while still letting you access the full power of a programming lanugage whenever you need it via the lozenge ◊ special character.

What I'm looking for, basically:

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: code in this context
Error phase
:compile-syntax-check
Location
Lines 51-56, Columns 1-358
Source expression
 (code
"I'd like the ability to embed ◊(link hiccup \"https://github.com/weavejester/hiccup\")/clojure data structures into my textual input. They can either be data (see below) or functions called at render time that evaluate into data (see above).

[:em {:class \"topic\"} Extensibility]

Clojure already supports this notion in its canonical representation of data, extensibile data notation. I want to bring it to textual information, and maybe, HTML Canvas objects as well. The full power of a programming language means that we can flexibly switch between graphical and textual representations, something that pollen doesn't yet support.") 

This approach also acts as a force-multiplier on immutable, compositional CSS tools like

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 58, Columns 90-114
Source expression
(in-code "tachyons")

or

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 58, Columns 118-142
Source expression
(in-code "tailwind")

, because it brings the power of Clojure into the tool you're using to write the text, which in turn leverages tools like

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 58, Columns 264-288
Source expression
(in-code "tachyons")

to apply a unified style to what you're writing using inline, simple notation.

Other examples in this space:

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: ul in this context
Error phase
:compile-syntax-check
Location
Line 62, Columns 1-45
Source expression
 (ul (in-code "idyll") (in-code "mdx")) 

Both of these are built atop Javascript, and are more focused on interactivity for users than on procedural generation of text at write-time. Clojure(script)'s homoiconicity makes it ideally suited for both purposes- it can be used to generate interactive programs as well as any other form of data you'd want to display. I'm personally more interested in the latter, right now.

Extensible textual notation, part 2

Within the Clojure world and beyond, there are a few tools that suggest directions for what I'm thinking of here.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 70, Columns 1-30
Source expression
 [:h4 (in-code "perun")] 

This is what I'm using to write and compile the blog itself right now. However, I don't like that most of the decisions about how to parse markdown into HTML are decided by the fiats of

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 72, Columns 187-216
Source expression
(in-code "flexmark-java")

I would much prefer to be able to manipulate the content in the form of

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 72, Columns 290-312
Source expression
(in-code "hiccup")

data structures as I see fit

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 72, Columns 343-360
Source expression
(em "before")

passing it to

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 72, Columns 375-408
Source expression
(in-code "hiccup.core/html5")

for rendering.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 72, Columns 424-504
Source expression
(link "https://github.com/hashobject/perun/issues/30" "This was discussed") 

in the

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 72, Columns 512-533
Source expression
(in-code "perun")

repo, but was set aside when the use case of hyphenation didn' t actually require it.

However, there are many other reasons you'd be interested in representing your writing as data.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: blockquote in this context
Error phase
:compile-syntax-check
Location
Lines 75-78, Columns 1-139
Source expression
 (blockquote
    {:author "Matthew Butterick"
     :url "https://docs.racket-lang.org/pollen/second-tutorial.html"}
    "if the book is a program, the source for that book should look more like your brain, and less like HTML (or XML or LaTeX or ...)?")

Personally, I'm interested in using the most powerful tools I have. For example, you could parse the writing into discrete chunks represented as

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 80, Columns 146-168
Source expression
(in-code "hiccup")

data structures, record them as facts in a

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 80, Columns 212-284
Source expression
(link "https://github.com/tonsky/datascript" (in-code "datascript"))

DB, and query them like any other source of data. This would be even more powerful if you ran it against not just the current state of your writing, but its revision history.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 82, Columns 1-86
Source expression
 (header {:level :h4} (link "https://github.com/metasoarous/oz" (in-code "oz"))) 

As a Clojure enthusiast who got started with Jupyter notebooks, I quite like the idea of using Clojure for interactive documents. I just wish Markdown wasn't so uncritically accepted as the default for text authoring, because it seems silly to give yourself the whole power of a programming language in rendering a document and then arbitrarily restrict its scope to making graphs. Scientific documents in particular deal with lots of structured data: batteries of tests, statistical analyses, summary tables. Presentation of that data is not limited to graphs: a more powerful authoring model would allow you to dynamically generate and restructure the prose annotations of scientific data as easily as the graphs that summarize it.

Anything less feels like an arbitrary step backward.

Code documentation tools

The major area in which programmers

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 90, Columns 37-57
Source expression
(em "currently")

perform programmatic manipulation of prose and data in tandem is in the realm of documentation generators. In my experience, these tools fall into two broad categories:

Narrative-first tools like

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 93, Columns 45-85
Source expression
(in-code "sphinx, reStructuredText")

, etc. They have one primary benefit: if they're to be of any use at all they require the author to write a good amount of prose introducing the project, its rationale and purpose, and the main ways of interacting with it. However, in these tools, docs are generally separate from code - even if they live in the same repo, they're often in

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 94, Columns 335-356
Source expression
(in-code "docs/")

and can easily come out of sync with the actual code.

Code-first like any

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 97, Columns 39-79
Source expression
(in-code "javadoc, docco, Roxygen2")

, etc. These have the benefit of being much closer to the day-to-day work of developers and are much less likely to become out of sync with the code, because they are usually parsed out of docstrings and special comments and the process of updating documentation can be built into a project's deployment pipeline without much overhead. The drawbacks? You generally end up with a completely decontextualized list of classes or functions that doesn't inform or give examples of how you'd actually

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 98, Columns 489-503
Source expression
(em "use")

them.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 100, Columns 1-97
Source expression
 (header {:level :h6} (link "https://gdeer81.github.io/marginalia" (in-code "marginalia"))) 


Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 102, Columns 1-27
Source expression
(in-code "marginalia")

generates elegant-looking literate programming documents from plain Clojure source code. Like

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 102, Columns 122-143
Source expression
(in-code "perun")

, however, it returns rendered HTML rather than structured data from its parsing of source files.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 104, Columns 1-83
Source expression
 (header {:level :h6} (link "https://github.com/namuol/cod" (in-code "cod"))) 

As a code documentation tool,

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 106, Columns 31-50
Source expression
(in-code "cod")

feels like it has the right idea at its core. Instead of deciding how to present the documentation it pulls out of the source code for you,

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 106, Columns 191-210
Source expression
(in-code "cod")

simply returns JSON data representing the annotations. Any further decisions about how to represent that JSON data in the final documentation are up to the author, allowing for a better blend of narrative and code than other documentation tools.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 108, Columns 1-97
Source expression
 (header {:level :h6} (link "https://docs.racket-lang.org/scribble/" (in-code "scribble"))) 

The fact that Racket libraries tend to have

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 110, Columns 45-62
Source expression
(em "vastly")

superior documentation (on average) than any other programming language is a testament to the power of Scribble. Naturally,

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 110, Columns 187-209
Source expression
(in-code "pollen")

owes a lot to the starting point that

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 110, Columns 248-272
Source expression
(in-code "scribble")

created.

Why look at code documentation tools?

Mostly because I know that I'm going to have to write my own solution to this problem. I want the solution's source code to itself generate an example of the kind of document I want it to produce, so I'm hoping I can steal as many existing functions as possible from these other libraries while I'm bootstrapping the project.

Structural features of writing and information management systems

I've gone through myriad to-do apps, organizers, journaling systems. Here's a table depicting my overall thoughts.

TypeDisadvantagesExamplesAdvantages
Binder notebookatemporal,apresentistFilofaxassociative,organic,frictional,multi-modal,simple
DiaryapresentistBullet journalchronological,frictional,reflective,multi-modal,simple
To-do appdecontextualizedNozbe, todoistfast,simple,portable
Kanbandecontextualized,information-poorTrellosituated,simple
free-form/wikihierarchical,laboriousNotionassociative,compositional,iterative
Websitelaborious,atemporalThisfrictional,multi-modal,associative

All of these advantages and disadvantages stem from one real underlying issue, in my view: each tool imposes its own view over the data you put into it, making alternative ways of looking at the same information difficult or impossible. Paul Chiusano has

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 147, Columns 256-343
Source expression
(link "https://pchiusano.github.io/2016-10-13/view-inspired.html" "written nicely")

about the conceptually weak data model an "intuitive" design imposes on the information it represents:

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: blockquote in this context
Error phase
:compile-syntax-check
Location
Lines 149-153, Columns 1-777
Source expression
(blockquote
  {:author "Paul Chiusano"
   :url "https://pchiusano.github.io/2016-10-13/view-inspired.html"
   :source "The design failures of view-first"}
  "We often think about views first because views are concrete, and it’s what we interact with directly when we use software. But actually designing software ‘view first’ is problematic because it leads to rigid models that aren’t flexible enough to support the myriad of creative ways that people use your software. It also leads invariably to feature creep—when your model is overly influenced by some concrete views you had in mind during design, it invariably ends up insufficiently general purpose. So as your software becomes more popular, you start adding one-off ‘features’ to support concrete use cases that your users are asking for. A few years pass of this feature creep, and you have a bloated, complicated piece of software that no one gets joy out of using.")

Every to-do list and knowledge management system suffers from this problem, In fact, I can feel the constraints imposed by the table above limiting what I want to say about each tool, so let's dive into what I mean by each of these words:

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: strong in this context
Error phase
:compile-syntax-check
Location
Lines 157-176, Columns 1-3
Source expression
 [:ul
[:li (strong "associative") " - topics and items added at different times can be seen side-by-side, permitting recontextualization of existing information."]
[:li (strong "organic") " - order emerges from what is added rather than being imposed."]
[:li (strong "frictional") " - the extra effort required to add additional material actually performs useful work rather than being a hindrance (a benefit that has thus far made handwritten notes more valuable to me than digital ones)."]
[:li (strong "multi-modal") " - multiple systems of representation can be easily employed in the same context."]
[:li (strong "atemporal") " - the system has no direct representation of the temporal ordering of its contents."]
[:li (strong "chronological") " - the system has a direct representation of the temporal ordering of its contents."]
[:li (strong "reflective") " - the system provides opportunities for reflection."]
[:li (strong "apresentist") " - the system has no direct representation of what's \"current.\""]
[:li (strong "fast") " - adding information is quick and reliable."]
[:li (strong "simple") " - the system itself does not impose barriers to adding additional information."]
[:li (strong "portable") " - information can be added and recalled through multiple mechanisms or devices."]
[:li (strong "decontextualized") " - information or items are cut off from their surrounding context."]
[:li (strong "non-iterative") " - the system does not support the process of refining information added to it; it expects items in their \"final state.\""]
[:li (strong "deep") " - the system supports long-form treatments of the information added to it"]
[:li (strong "hierarchical") " - the system requires information to be organized in a tree format, thwarting associational views of it."]
[:li (strong "situated") " - the system provides useful background information without getting in the way of the work the information is intended to support."]
[:li (strong "compositional") " - underlying data, through association, can be " (em "composed") " into higher-level information."]
[:li (strong "laborious") " - the effort required to add or revise material imposes costs rather than providing benefits."]]

Most recently, I've been using a Zettelkasten-style system for my notes with a filofax binder. It's superb for free association, quick entry, and the generative friction that only putting pen to paper can provide. It's not so good for revisiting previous notes, synthesizing them into new information, reflecting on the past, or maintaining a view of what's "current." Before that, I used a journal-style notebook that was similarly good at quick free-form entry and helped maintain a chronological view of things that aided in reflection, but failed to support associational views of the information recorded within it and similarly suffered from difficulties in keeping things current. I think a two-phase system that facilitates the refinement of paper "drafts" into digital "facts" would be ideal for me, personally.

Many digital systems for doing this exist already. I chafe at using them because they all uncritically accept that markdown is a useful format for representing semantically rich textual information, and then shoehorn features on top of it to make up for its limitations.

Obviously, I'm also taking notes here instead of on paper. Writing this doesn't provide exactly the same generative friction as pen and paper, but does a good enough job of forcing me to clarify my thoughts through the pressure of putting them in a public format. I also have complete control over the content (once I can overcome the limitations of markdown). Given that what I write has currently a 1:1 file:destination relationship, it also prevents association and composition of the information I record here. Ideally you'd want to break this input/output link, which would support both private views of some information and also let you think about how to refer to the same piece of information from multiple public views.

The question of how to

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 184, Columns 24-129
Source expression
(link "https://plato.stanford.edu/entries/information-semantic/" "individuate pieces of information")

is permanently open, so an ideal system would support "contention" in that it can facilitate multiple methods of splitting up and representing a topic. How to do that on a technical level is obviously also an open and extremely difficult problem.

It seems daunting to come up with a solution for this, but I've been reading about something that may offer a partial way out recently: Datascript, mentioned in passing earlier. Where Chiusano proposes algebraic data types to manage this, I would prefer to start with datoms that get freely composed into views through datalog queries. Pieces of information (or even bits of writing themselves) would be decomposed down into EAVT facts and recorded in some persistent database where they can change in the future without fear of losing knowledge by revising it.

There's a lot more to say on the design of this, but mostly I wanted to get this concept "on paper" for further development into a design.

Structural features of writing and information management systems, part 2

Another distinction that cuts across everything that I referenced in that table above is the idea of

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 192, Columns 102-119
Source expression
(em "closed")

versus

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 192, Columns 127-142
Source expression
(em "open")

knowledge management systems. While my notebook has acquired a significant amount of internal complexity, it is largely a

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 192, Columns 265-283
Source expression
 (em "closed")

system, making interaction with other sources of information more difficult. I have a gigantic pinboard backlog, highlights in a kindle, scattered paper notes about physical books, and no means of integrating them or refining them into something more meaningful.

A lot of PIMs designed to support academic reseearch are "open" towards producing and consuming the primary objects of academic research: papers and monographs. I'm not an academic. While writing helps me clarify my ideas, I also need tools oriented towards the work I do in programming, which means supporting a more

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 194, Columns 319-338
Source expression
(em "situated")

understanding of what I'm doing. By that I mean supporting a "keep this in mind as you act" understanding of something rather than a "discrete textual description" understanding of something. In cybernetics terms, one might say that my information management systems have not had the

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 194, Columns 623-651
Source expression
(em "requisite variety")

to handle the tasks I want them to support. They are not

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 194, Columns 709-724
Source expression
(em "open")

to non-textual workflows.

Here's a quick sketch of what this might be look like:views-sketch

The bottom has a pomodoro-style task tracker and the "current task", the right pane has a grouping of recent commits to keep the actual output of that task in mind as well. The role of these panes isn't the important part - the mechanism by which they're generated is. By pulling information from a common store, simple contextual visualizations of relevant parts of it would be easy to construct via Datalog queries.

A further source of information comes from the seemingly simple fact that these pieces of information are

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 202, Columns 107-136
Source expression
(em "displayed together")

. The entities referenced by the views currently active could be linked through additional queries - for example, the commits happening in the text editor could create corresponding entities with attributes linking them to the entity of the current task. Similarly, information entries updated when a text file is open or a namespace is edited could be linked with that text file. This establishes a notion of

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 202, Columns 546-566
Source expression
(em "relevance")

for the supporting materials of the work being done.

This website (could be) a CRDT

While considering potential applications of

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 206, Columns 45-77
Source expression
(link "/relay.html" "relay")

software, I recalled the notion of a

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 206, Columns 115-160
Source expression
(em "conflict-free replicated data type")

, a data structure that provides a probably correct solution to the problem of imposing a total order on a sequence of edits to a file that arrive out of order, editing different subsets of text, with unreliable timestamps. This data type would be what you reach for if you were designing a collaborative text editor with online and offline editing capabilities, because it would save you from making hard choices about which text to discard and which to keep (or worse, making the user deal with any errors caused by your software and imposing those choices on them).

I started reading about the concept, glossing over the mathematical details in favor of an interest in its potential as an expressive medium for thought. Some ideas that fell out of this:

Making the library metaphor in 'code library' concrete


Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 212, Columns 1-121
Source expression
 [:aside "heavily inspired by Rich Hickey's talk " (link "https://youtube.com/watch?v=oyLBGkS5ICk" "Spec-ulation")] 

Right now you have to take home the whole library when you write some code that uses one page of one book.

Statically typed languages that rely on complex class hierarchies, especially because the compiler may make multiple passes across the codebase for definitions in different files ( ... all you wanted was the banana. - Joe Armstrong) force you to ship all this supporting material to use one part of it.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 218, Columns 1-685
Source expression
 [:aside  "I don't intend this as an intrinsic dig at statically typed, compiled languages per se. Smart compilers can do dead code elimination, but usually this technique is put to the purpose of reducing " (em " executable ") "size rather than reducing" (em " dependency ") "size. It'd be very interesting to see a compiler targeting library code that minimizes the volume of the library code pulled in by the code which declares it as a dependency. " (link "https://www.unisonweb.org/docs/tour" "Unison") " has done some interesting work in this direction because of its ability to serialize algebraic data types and send them over the network to perform remote computation."] 

So if instead of classes defined across files or nested relationships between algebraic data types defined at compile time, we had functions operating on simple, immutable values defined in self-contained s-expressions, plus some annotations:

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: code in this context
Error phase
:compile-syntax-check
Location
Lines 222-226, Columns 1-4
Source expression
(code
"(defn myfunc
 {:calls #{this.ns/func other.ns/func}
...)"
)

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 227, Columns 1-162
Source expression
(em "this could maybe be achieved even without the manual annotation if you used a macro to pull the symbols out of the function expression at compile time")

Rather than a scope defined by a global namespace of evaluated expressions, these explicit references define exactly what a function needs to be lifted out of its lending library and used independently of the codebase it came from.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 231, Columns 1-423
Source expression
 [:aside  "to make an analogy to Unison above, using " (in-code "spec") " plus these dependency annotations means that the functions would be addressed by " [:em "contract"] " rather than by " [:em "content."] " I think that Unison's emphasis on making functions immutable is a good one, but annotating the contract rather than the internals may do a better job of preserving intent for a dynamically typed language."]

S-expressions would slot naturally into the delimited data structures required by a CRDT, making this serialization easy (other programming languages may have a harder time). This opens up another application:

New forms of revision control

CRDTs can contain arbitrary series of revisions to the same underlying data, in a method guaranteed to converge on a consistent result.

Documentation could be stored in the same CRDT. If the documentation has old timestamps, a tool could be built atop them to warn the user or author that they're stale relative to the rest of the code. Test results could be stored with the hash of the CRDT at the time they were executed, making failing tests trivial to reproduce. With a clever index, the failing tests associated with a given function could be recalled from the codebase's history with a query, providing useful context for finding the source of an unexpected regression.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: aside in this context
Error phase
:compile-syntax-check
Location
Line 241, Columns 1-192
Source expression
(aside "again, this requires a language with an unambiguous syntax and referential transparency to be truly effective - I make no claims to being fair to non-lisp programming languages.")

Configuration for external systems, or expressions that modify it, could be stored in the same CRDT as the code itself. Integration and system tests could be linked with configuration changes in the manner I describe above, providing context for when the components of a distributed system fail and are made to work again.

Tests could be shipped around with their functions using a similar annotation syntax to the one above so that someone can have guarantees about the external code they're relying on.

A notebook for the table beside your hammock

If code and documentation are part of the same data structure as a whole, then an "ideas first" approach to software is as easy to start and maintain as a new experimental repo. The recorded ideas can evolve in tandem with the code that implements them, and their interplay gets expressed through the immutable history of the data structure recording them. It's an environment that makes hammock-driven development as easy as flow-state coding and bug squashing, with the ability to fluidly switch between them without breaking the flow. Code itself as one component part of an open system that doesn't treat writing down the problem and writing the code that solves it as separate activities.

What else is possible? Right now, code takes on the shape that Git repositories, and the software we use to interact with them, want it to take. Can we break code revision history and reuse out of the paradigm of discrete individual repositories? Is a distributed data structure like this enough to make the distinction between "monolithic" and "microservice-oriented" code obsolete?

Alex Miller

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 253, Columns 13-83
Source expression
(link "https://news.ycombinator.com/item?id=20365854" "writes of")

the new model embraced by

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 253, Columns 110-134
Source expression
(in-code "deps.edn")

:

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: blockquote in this context
Error phase
:compile-syntax-check
Location
Lines 255-258, Columns 1-705
Source expression

  (blockquote
  {:author "Alex Miller"}
  "deps was designed to find a sweet spot in the middle of this with deps defined as data, aliases capturing program executions as data, but builds as programs. As such, the scope is drastically narrowed in deps to just a) building classpaths (by resolving dependency graphs) and b) launching programs. As such, this tends to be a dramatically simpler model to start with (your initial deps.edn can be empty), and a model that is easy to understand as you scale up. I think there is more to do in how we model \"tools\" (esp tools shared across projects) and program composites, but nothing prevents you from building these yourself if needed (as you have the full power of Clojure at your disposal).")

Storing code in a CRDT has the potential to explore new parts of the misty space between "tools" and "programs" for Clojure code. I'm definitely interested in where this could lead, but I have to figure out how to create s-expressions from my prose first.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: aside in this context
Error phase
:compile-syntax-check
Location
Line 262, Columns 1-369
Source expression
(aside "Oh, and by the way, the formal term for the structure that emerges from a properly implemented CRDT is a " (link "http://archagon.net/blog/2018/03/24/data-laced-with-history/" "monotonic semilattice") ". Which, according to Christopher Alexander in the essay I quote above, is exactly the form required to capture the interdependent complexity of a city.")


Extensible textual notation, part 3


Structure from text

I want to replicate

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 268, Columns 21-43
Source expression
(in-code "pollen")

's ability to let prose be prose while still incrementally bringing in a programming language when it's needed, but also combine it with Clojure's own data structures to capture the structure that emerges organically from the act of writing, so I could, for example, capture the table above not just as a sequence of textual elements but also preserve the structure of the tabular data itself for future use somewhere else.

The simplest implementation of that would be just reading in the file line by line and constructing maps from the paragraphs separated by line breaks:

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: code in this context
Error phase
:compile-syntax-check
Location
Lines 272-277, Columns 1-5
Source expression
(code 
"{:id e4268ac2
 :text \"Paragraph one.\"}
{:id e4268ac3
 :text \"Paragraph two.\"}
")

The initial reading process creates entities that serve as placeholders for text as it is when read and as it may be in the future, all recorded as facts in a EAVT/RDF semantic triple format. Knowledge atoms instead of data atoms. But a collection of facts doesn't preserve the ordering of their original composition, which is a lot of structure to throw away. There are two ways of preserving it that initially occurred to me:

[1]

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: strong in this context
Error phase
:compile-syntax-check
Location
Line 281, Columns 5-42
Source expression
(strong "files are entities too")

- just have them refer to their contents as distinct entities.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: code in this context
Error phase
:compile-syntax-check
Location
Lines 283-290, Columns 1-5
Source expression
(code 
"{:entity 23542
 :attribute :filename
 :value \"plaintext-file.txt\"}
{:entity 23542
 :attribute :contents
 :value [52952 29587 29042]}
")

In this mode, order of paragraphs is asserted as a fact on the basis of the vector of entity ids of the constituent paragraphs.

[2] Alternatively, the facts about the paragraph order could just be

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: strong in this context
Error phase
:compile-syntax-check
Location
Line 294, Columns 70-110
Source expression
(strong "composites of other facts")

:

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: code in this context
Error phase
:compile-syntax-check
Location
Lines 296-301, Columns 1-5
Source expression
(code 
"{:entity 23542
  :attribute :contents
  :value [{:uuid ab50234 :text \"opening paragraph goes here\"}
          {:uuid ab50235 :text \"second paragraph goes here\"}]}
")

I don't really like 2. it feels ad-hoc and non-relational, whereas 1 seems more relationally correct but is semantically not as rich as an individual fact. This shortcoming is easily resolved by a query to pull in the relevant text, however.

Speaking of which:

Text from structure

When thinking about where to store this data, I was led to Chris Smothers'

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 309, Columns 76-140
Source expression
(link "https://github.com/smothers/cause" (in-code "cause"))

, a very well-documented Clojure implementation of a causal tree, a type of CRDT. It places the notion of a

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 309, Columns 248-274
Source expression
(in-code "CausalBase")

front and center, which sounds great, except it doesn't quite have the power implied by the "database" referred to by its name - which is generally okay in Clojure because the language already has pretty powerful facilities for quick operations on collections of maps.

But what if someone went further than that, combining a CRDT with a data model and query engine like in DataScript? Turns out in describing that I'm describing

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 311, Columns 161-233
Source expression
(link "https://github.com/replikativ/datahike" (in-code "datahike"))

, a Datalog implementation atop the

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 311, Columns 269-299
Source expression
(in-code "hitchiker-tree")

CRDT.

With existing text snapshotted as facts and recorded in a CRDT, queries could be run against that data to associate formerly disparate pieces of data into new forms, and the composites those queries create could themselves be recorded and annotated as new facts about the collection. The query that retrieves those facts could be stored as data itself, with the new structure that the query identifies added as an annotation to it. Use these queries and the expressive power they create to give a new life to

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 313, Columns 510-534
Source expression
(in-code "structur")

and

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 313, Columns 539-560
Source expression
(in-code "alpha")

, the venerable software extensions to

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 313, Columns 599-620
Source expression
(in-code "Kedit")

written by Howard J. Strauss to aid

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 313, Columns 657-763
Source expression
(link "https://www.newyorker.com/magazine/2013/01/14/structure" "John McPhee in his writing process.")


He listened to the whole process from pocket notebooks to coded slices of paper, then mentioned a text editor called Kedit, citing its exceptional capabilities in sorting. Kedit (pronounced 'kay-edit'), a product of the Mansfield Software Group, is the only text editor I have ever used. I have never used a word processor. Kedit did not paginate, italicize, approve of spelling, or screw around with headers, WYSIWYGs, thesauruses, dictionaries, footnotes, or Sanskrit fonts. Instead, Howard wrote programs to run with Kedit in imitation of the way I had gone about things for two and a half decades.

He wrote Structur. He wrote Alpha. He wrote mini-macros galore. Structur lacked an “e” because, in those days, in the Kedit directory eight letters was the maximum he could use in naming a file. In one form or another, some of these things have come along since, but this was 1984 and the future stopped there. Howard, who died in 2005, was the polar opposite of Bill Gates—in outlook as well as income. Howard thought the computer should be adapted to the individual and not the other way around. One size fits one. The programs he wrote for me were molded like clay to my requirements—an appealing approach to anything called an editor.

Structur exploded my notes. It read the codes by which each note was given a destination or destinations (including the dustbin). It created and named as many new Kedit files as there were codes, and, of course, it preserved intact the original set. In my first I.B.M. computer, Structur took about four minutes to sift and separate fifty thousand words. My first computer cost five thousand dollars. I called it a five-thousand-dollar pair of scissors.

I wrote my way sequentially from Kedit file to Kedit file from the beginning to the end of the piece. Some of those files created by Structur could be quite long. So each one in turn needed sorting on its own, and sometimes fell into largish parts that needed even more sorting. In such phases, Structur would have been counterproductive. It would have multiplied the number of named files, choked the directory, and sent the writer back to the picnic table, and perhaps under it. So Howard wrote Alpha. Alpha implodes the notes it works on. It doesn’t create anything new. It reads codes and then churns a file internally, organizing it in segments in the order in which they are meant to contribute to the writing.

Alpha is the principal, workhorse program I run with Kedit. Used again and again on an ever-concentrating quantity of notes, it works like nesting utensils. It sorts the whole business at the outset, and then, as I go along, it sorts chapter material and subchapter material, and it not infrequently arranges the components of a single paragraph. It has completely served many pieces on its own.

John McPhee


Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 329, Columns 1-74
Source expression
(link "https://docs.racket-lang.org/pollen/" "The book is a program")

. Tools for writing digital books should be at least as powerful as the tools created for conventional books decades ago. CRDTs provide a reliable and immutable foundation to the discrete chunks of knowledge that McPhee has used for his entire career. A query engine provides the toolkit to devise new ways of composing them together as powerful as

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 329, Columns 423-447
Source expression
(in-code "structur")

and

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 329, Columns 452-473
Source expression
(in-code "alpha")

, but with the added benefit of an entire programming language so that the text (or the collection of notes used to produce it) is no longer a closed system but can instead pull in data from the rest of the world.

The Markdown Cargo Cult


Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: blockquote in this context
Error phase
:compile-syntax-check
Location
Lines 333-334, Columns 1-63
Source expression
(blockquote {:author "Matthew Butterick" :url "https://docs.racket-lang.org/pollen/second-tutorial.html"}
               "First and worst, Markdown isn’t semantic.") 

I view basically every other problem with Markdown as downstream from this. Like Butterick, I'm utterly baffled by the degree to which everyone developing new types of interactive authoring tools simply assumes that everyone will want to write text in a format that's completely blind to the organic structure that emerges from ordinary writing.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 336, Columns 347-486
Source expression
(link "https://jupyter-notebook.readthedocs.io/en/stable/examples/Notebook/Working%20With%20Markdown%20Cells.html" "Jupyter notebooks")

,

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 336, Columns 489-547
Source expression
(link "https://www.mkdocs.org/" "documentation tools")

,

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 336, Columns 549-692
Source expression
(link "https://github.com/metasoarous/oz/blob/be700e721fd758024f0783083279132afc42f317/examples/test.md" "interactive documentation tools")

,

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 336, Columns 694-874
Source expression
(link "https://github.com/nteract/nteract/blob/f94502e4ff654bb58166bff262f133d4f449b049/packages/outputs/src/components/media/markdown.md" "interactive data science toolboxes")

,

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 336, Columns 876-963
Source expression
(link "https://idyll-lang.org/docs/syntax" "JS-based explorable explanation tools")

,

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 336, Columns 966-1146
Source expression
(link "https://github.com/witheve/Eve/commit/fa1700cb37198d1a02ebbaaa506c70c40b201d76" "revolutionary new prototypes of combined programming languages and visual environments")

,

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 336, Columns 1148-1338
Source expression
(link "https://github.com/mhuebert/maria/blob/88776252f16ccacb23fb63d83223186b8cd55f8b/editor/src/maria/commands/prose.cljs" "further explorations of how programming could be different")

, all of them voluntarily choosing to tie the millstone of this impoverished format around their necks despite serious attempts to rethink the combination of code and prose.

Why do we use it? Because one of

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 338, Columns 34-157
Source expression
(link "https://daringfireball.net/projects/markdown/" "Apple's court intellectuals decided it was convenient for him?")


Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: blockquote in this context
Error phase
:compile-syntax-check
Location
Lines 340-343, Columns 1-380
Source expression
(blockquote
  {:url "https://news.ycombinator.com/item?id=16230676"
   :source "Hacker News"}
  "How is Markdown innovative exactly? It took ideas from the 70s, dropped the interesting parts, and was hailed as a revolutionary approach to marking up documents. Ie, the past 30 years of computing have been about narrowing the interface between programmer and computer to the equivalent of a straw (everything as text!) and then try to build an entire system around that.")

Spotted on Hacker News, the only reasonable response to someone calling Markdown 'a triumph of programmer ergonomics.'

Every system built atop Markdown will invariably have some ad-hoc and kludgy method of attempting to recapture some part of the structure that emerges from text authored in markdown, and it will be different from every other one because Markdown is blind to structure in all but the most basic of ways. In that regard it is very similar to "plain-text configuration" tools like

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 347, Columns 379-421
Source expression
(link "/against-metadata.html" "YAML")

, which have all kinds of templating engines bolted on to them to overcome the limitations of what has in practice become a flat-file key-value store.

Just be aware of what you're giving up as an author in pursuit of that, and what you may be imposing on yourself later on down the line if you want to overcome these constraints.

And yes, there's no small irony in the fact that the

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 353, Columns 54-219
Source expression
(link "https://github.com/respatialized/respatialized.github.io/blob/75691228fc403a05e9184cbb7d2a930eb40ffbf4/content/not-a-tree.md" "source code for this post")

is currently written in Markdown. It is indeed fast and easy to get started writing with it, but I'd largely attribute that to path dependence, and the fact that my particular parser leaves the

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 353, Columns 414-433
Source expression
(in-code "div")

tags I've littered throughout this post intact, which is an accident of choosing to use

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 353, Columns 522-543
Source expression
(in-code "perun")

and thereby

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 353, Columns 556-585
Source expression
(in-code "flexmark-java")

rather than the virtues of the format itself. I have every intention of changing the authoring tool I use into something semantically richer, but I had to get my resistance to the format on paper first.

Extensible textual notation, part 4


Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: blockquote in this context
Error phase
:compile-syntax-check
Location
Lines 357-359, Columns 1-362
Source expression
 [:section {:columns 5}
      [:div {:span 2} (image "media/thinking-about-things.jpg" "laptop sticker reading 'thinking about things'" "mw20")]
  [:div {:span 3} (blockquote "I read relentlessly. I don’t do any programming not directed at making the computer do something useful, so I don’t do any exercises. I try to spend more time thinking about the problem than I do typing it in." [:br] (link "http://web.archive.org/web/20160918041754/http://codequarterly.com/2011/rich-hickey/" "Rich Hickey"))]] 



Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 363, Columns 1-90
Source expression
(header {:level :h5} "Beyond plain text: storing prose within " (in-code "datahike"))

Here's a

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 365, Columns 10-118
Source expression
(link "https://blog.datopia.io/2018/11/03/hitchhiker-tree/" "background post on the Datahike internals")

for context about how the hitchhiker B-tree structure allows for self-balancing and efficient updates that "hitchhike" on queries.

Here's another on using the

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 367, Columns 29-51
Source expression
(in-code "dat://")

protocol for

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 367, Columns 65-211
Source expression
(link "https://lambdaforge.io/2019/12/08/replicate-datahike-wherever-you-go.html" "P2P replication of the data stored in a Datahike instance")

. It serves as a useful starting point for getting a Datahike instance up and running.

Here's what would be a useful starting point for programmatic prose parsing: including a quotation in a piece of prose writing that gets parsed as a separate component and then added to a global list of quotations maintained by the text parser, with a link back to its original positional context within the piece of writing that quoted it.

Extensible textual notation, part 5


A concrete starting point

I've managed to come up with a lot of Xanadu-like vaporware ideas in thinking through this tool without producing anything concrete.

Per the above: I'd define my first concrete goal for this library as a replacement for markdown so I can begin to dig myself out of the pit I've put myself in by relying on something I don't like using.

In order to do this, I want to parse markdown into

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 379, Columns 52-74
Source expression
(in-code "hiccup")

and pull information out of the file. Whatever replaces markdown will use

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 379, Columns 149-171
Source expression
(in-code "hiccup")

data structures anyway, so it's not wasted effort to build functions that process the markdown once it's represented as Clojure data. I can create functions and

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 379, Columns 333-353
Source expression
(in-code "spec")

s that define the expected behavior of a markdown replacement.

Based on some unscientific experimentation, the only markdown->hiccup toolchain that properly understands tables is

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 381, Columns 117-155
Source expression
(in-code "markdown-clj + hickory")

, so that's what I'll go with. I have a few tests written that don't do much yet.

Other scattered thoughts:

Plaintext and database

Plain text has a lot of virtues as a long-term storage format, so I plan to make it a core part of however I persist the writing that gets parsed into data by ETN.

The current snapshot and any derived views of it exist should exist as plaintext; its history can be preserved using database backup and persistence methods. But perhaps other defined snapshots in the history of the information should be serialized as plain text as well, in a manner similar to git commits or releases.

Thoughts on Roam

I signed up for Roam because on paper it seems to be exactly what I want: a PIM with the ability to run arbitary Datalog queries across your thoughts and embed hiccup data structures for visual depictions of the concepts. It's built on Clojure, front to back! What's not to like?

Mostly, the UX. I don't like the aggressively hierarchical format it imposes on all the writing you put into it, I don't like the web interface, which will never be as fast and flexible as plaintext with a good editor, and I don't like the default views it chooses for you.

More than anything, I want a tool of my own making, free from any compromises made to accomodate commercial success or adoption among its target cohort.

I don't want an outlining tool that helps me produce writing. I want a

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 399, Columns 72-90
Source expression
(em "writing")

tool that helps me identify and work with the structure that emerges from what I write.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: aside in this context
Error phase
:compile-syntax-check
Location
Line 401, Columns 1-282
Source expression
 (aside "that and the founder is building the core product around the demands of the LessWrong crowd and goes on [1/178] twitter rants about stuff he reads on SlateStarCodex. the app feels like the product of that kind of approach: unfocused and built around pseudoproblems.") 


Extensible textual notation, part 6

Yesterday I got too caught up reading the documentation for libraries. Today I'm disabling my wifi and striking out into the wilderness with only the standard library (and my reference book) to help me.

First discovery: I probably don't need to use

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 407, Columns 47-70
Source expression
(in-code "specter")

when

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 407, Columns 76-100
Source expression
(in-code "tree-seq")

will do.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 407, Columns 110-138
Source expression
(in-code "clojure.walk")

will also help, but I don't quite understand it yet.

One thing that occurred to me when thinking about pulling quotes out of plaintext: while an individual paragraph should be the basic semantic unit of my own writing, the basic semantic unit of a quotation or reference should be a sequence of

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 409, Columns 243-265
Source expression
(em "one or more")

paragraphs. This preserves more of the structure of the origin and aids in its display in other contexts. For storage purposes, though, it should merely be (for now) a sequence of strings. Worrying about the internal structure of the quote itself (lists, etc) can come later when the specs get more refined.

Extensible textual notation, part 7

With a markdown->hiccup parser in hand, I took on a warm-up exercise to map out the problem space and get comfortable with parsing the data I've already dumped in to these markdown files, I defined some contracts for the data formats I want to pull from the textual information using

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 413, Columns 286-314
Source expression
(in-code "clojure.spec")

, which will serve as constraints on the expected behavior of other parsers I write to replace Markdown. It's pretty bare-bones so far, with stuff like the following:

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: code in this context
Error phase
:compile-syntax-check
Location
Lines 415-437, Columns 1-5
Source expression
(code
"(defn same-size? [colls]
  (apply = (map count colls)))
(defn col-kvs? [table-map] (spec/valid? (spec/map-of string? vector?)
                                        (dissoc table-map ::table-meta)))

(spec/def ::same-size same-size?)
(spec/def ::eq-columns #(same-size? (filter sequential? (vals %))))
(spec/def ::col-kvs col-kvs?)

(spec/def ::table-whole-meta map?)
(spec/def ::table-body-meta map?)
(spec/def ::table-header-meta map?)

(spec/def ::table-meta
  (spec/keys :req [::table-whole-meta ::table-body-meta ::table-header-meta]))

(spec/def ::tidy-table
  (spec/and
   (spec/keys :opt [::table-meta])
   ::col-kvs
   ::eq-columns))
")

These plus some functions to transform parsed

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 439, Columns 47-69
Source expression
(in-code "hiccup")

data structures into these canonical formats will allow me to capture some of the emergent structure of what I've already written here. Using

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 439, Columns 212-285
Source expression
(link "https://github.com/Provisdom/spectomic" (in-code "spectomic"))

I can define the constraints using spec and automatically generate

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 439, Columns 353-377
Source expression
(in-code "datahike")

schemas from them.

But that's only the starting point. The real purpose here is to replace

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 441, Columns 73-97
Source expression
(in-code "markdown")

with a notation format that represents

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 441, Columns 137-160
Source expression
(em "text as data")

and lets the user easily convey other types of structured data within the text itself. To that end, I have to come up with a different format for notating the documents, a very brief example of this I sketched out above.

There is plenty of prior art for this: I generated the first version of this blog using

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 443, Columns 89-111
Source expression
(in-code "pollen")

. I wanted to learn about the way

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 443, Columns 145-169
Source expression
(in-code "scribble")

, on which

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 443, Columns 180-202
Source expression
(in-code "pollen")

is built parses plaintext into Racket data structures for manipulation, but the library is quite complex (the documentation is fantastic, but it focuses mostly on the API rather than how the parsers and readers are implemented interally). It also reimplements much of what I intend to use

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 443, Columns 492-514
Source expression
(in-code "hiccup")

to do.

Luckily, I have been spared the experience of suffering through the entire

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 445, Columns 76-100
Source expression
(in-code "scribble")

codebase by Bogdan Opanchuk's

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 445, Columns 131-211
Source expression
(link "https://github.com/fjarri/clojure-scribble" "Clojure implementation")

. I could simply use it directly, but I'm not interested in taking more shortcuts and adding more libraries to this project, especially when I know I'll have to add my own syntax to the notation and the parser rules to support them. I also won't really understand the way these parsers work unless I go forth and implement one myself. Fortunately, the project uses

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 445, Columns 576-602
Source expression
(in-code "marginalia")

to give a guided tour through the internals of the code. This library may not be as comprehensive as the original implementation of

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 445, Columns 735-759
Source expression
(in-code "scribble")

, but Clojure's expressivity makes it far easier to understand the global structure of this smaller implementation and thus learn from it.

Rather than a

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 447, Columns 15-112
Source expression
(link "https://spec.commonmark.org/" "plaintext spec that monotonically grows in complexity")

due to the workarounds to handle the corner cases generated by ambiguous syntax, I'm hoping to define as much of the expected structure using

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 447, Columns 255-283
Source expression
(in-code "clojure.spec")

, which strikes the right balance between the rigidity of a BNF grammar and the ambiguity of a plaintext spec, plus the additional leverage that comes with defining a spec as code: the ability to

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 447, Columns 479-583
Source expression
(link "https://clojure.org/guides/spec#_custom_generators" "generate arbitary adversarial examples")

to ensure that corner cases are found quicker and dealt with in a more systematic fashion.

Extensible textual notation, part 8

One motivation for this concept was an incredibly useful design exercise when I was building a backend system at work: creating a

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 451, Columns 131-207
Source expression
(link "https://www.evanmiller.org/feature-matrix.html" "feature matrix")

for the various sub-components to understand their interactions with one another and how those translate into both library code and user-facing features. (read Evan Miller's whole essay; it's a quick read and a succinct, lucid statement of a very powerful idea). In order to produce one, I had to step outside of my trusty text editor and flip over to Google Sheets to create a NxN grid, fill it up with the pair-wise interactions between the components, and then use an unholy spreadsheet formula to transpose those comma-delimited features into a discrete list with separate references to each column in the body of the matrix:

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: code in this context
Error phase
:compile-syntax-check
Location
Lines 453-455, Columns 1-5
Source expression
(code
"=unique(transpose(split(join(\"|\", 'Sheet1'!$B$3:$B$17, 'Sheet1'!$C$3:$C$17, 'Sheet1'!$D$3:$D$17, ... \"|\")))"
 )

It was the most beautiful

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 457, Columns 27-96
Source expression
(link "https://wiki.c2.com/?WaterfallModel" "waterfall planning")

I've ever done. As you'd expect, looking back over the matrix now, I see how hopelessly out of date it is and how badly it serves its original purpose of defining tasks with enough granularity to yield tickets.

The codebase is always the most up-to-date part of any software project. Everything else tends to lag behind, mostly due to the unavoidable fact that you don't always know what you need to build before you build it. But why can't we write documents in a way that lends itself a little better to the day-to-day work of software development? Why can't I plop a feature matrix right into my

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 459, Columns 389-411
Source expression
(in-code "readme")

and then programatically generate a set of test suites for the features within it? That way, by changing the top-level description of the project, I also change the definition of the software that ensures it functions as intended.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: aside in this context
Error phase
:compile-syntax-check
Location
Line 461, Columns 1-306
Source expression
(aside "please do not tell me that org-mode supports tables. I am not interested in a solution specific to emacs lisp that imposes a separation  between structured data and code by tangling out the code into separate files, and thus remains ignorant of the actual information generated by that code.")

You might say something like "design should be design, and code should be code. Just because a problem is represented a certain way in the design phase doesn't mean that the actual code should be laid out that way." I agree with part of the spirit behind this. Stepping away from the laptop to think through the problem is something that everyone should do more often. I deeply enjoy the more embodied sense of problem-solving that sketching on a whiteboard gives me. But sometimes, you need more than a sketch or description; structured data can represent facts about a codebase that spec documents and architecture diagrams cannot. Keeping this up-to-date means seeing and editing it directly in tandem with the code. It means automatically checking off one of the "to-dos" generated from the structured documentation when a given test passes. It means storing information about failed and successful builds not in some

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 463, Columns 924-1042
Source expression
(link "https://docs.gitlab.com/ee/ci/pipelines.html" "web interface that has no direct interaction with the code")

but perhaps in the same data structure as the code itself.

Saying something like "code should look like code" assumes that we can only have one canonical way of representing the data that ultimately forms the program. But a richer data structure than flat files (like a CRDT) could be made to form the backend of multiple representations, one "API-first" view according to the code layout and another "feature-first" view according to the table of requirements. What if you could filter down the test suites you run as easily as filtering a spreadsheet? (you might call this way of interacting with the code

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 465, Columns 550-588
Source expression
(em "view-inspired, model-driven")

, to borrow a term from Chiusano's essay I link above).

Sometimes it feels like our mental model of tooling for software still comes from a pre-network era, where the notion of the software

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 467, Columns 135-154
Source expression
(em "artifact")

prevails and tools for managing individual artifacts win the day. They define the software we write as a discrete

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 467, Columns 269-293
Source expression
(em "closed system")

, and then we bolt more complexity back on to get around this model. It's why we ship around many megabytes more code than we need to when checking out a function from a library and it's why revision control operates fundamentally at the level of a single folder rather than the sub-units defined by the code within that folder - we ship around the whole folder because that's what we have isolated and replicable history for. Software still yields artifacts: docker images, executable binaries, etc. That hasn't gone away (and it won't until we're all using ultra-live environments that live up to the legacy of the first Smalltalk systems), but now code is much more likely to be a part of a

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 467, Columns 987-1009
Source expression
(em "open system")

that interacts with build tools, clusters of virtual machines, live data stores, and the like. The mess of information those tools generate informs choices about the code we write, so why not figure out a way to represent the information we need closer to the code itself?

For one possible foundation for a different approach to managing the history of code, see Unison's concept of

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 469, Columns 111-185
Source expression
(link "https://www.unisonweb.org/docs/tour" "content-addressed code.")

Content-addressing and tracking the history of functions as individual units means that they can break out of their original repos while maintaining their lineage. Instead of snapshots of whole libraries, functions could migrate from one library to another, picking up unique changes as they propagate their descendants into that new project's context. Code would have a

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 469, Columns 557-577
Source expression
(em "genealogy")

rather than

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 469, Columns 590-613
Source expression
(em "dependencies")

. Similarly, I think there's a lot of potential power in storing arbitrary structured data in the same data structure as the code itself. We haven't really taken the conceptual leap towards developing applications around that model because we default to git for any new project and thus rely on its implicit view of the world. Hopefully that can change.

Extensible textual notation, part 9

Turns out I was more right about needing to implement my own

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 473, Columns 62-86
Source expression
(in-code "scribble")

-like syntax than I knew, because while taking the clojure implementation out for a spin, I discovered that it relies heavily on an

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 473, Columns 218-300
Source expression
(link "https://github.com/jwymanm/chiara" "unmaintained set of reader macros")

that are incompatible with recent versions of Clojure because

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 473, Columns 363-391
Source expression
(in-code "clojure.spec")

now enforces compile-time syntax of macros (like

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 473, Columns 441-461
Source expression
(in-code "defn")

).

I'm going to have to write my own text->EDN parser that replicates what

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 475, Columns 73-97
Source expression
(in-code "scribble")

relies on reader macros to do. I'm fortunately

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 475, Columns 145-159
Source expression
(em "not")

trying to alter in any fundamental way how Clojure data is transformed into its AST; I'm just doing some preprocessing on plaintext so it's the right shape when it hits the clojure reader. Into the wilderness.

Extensible textual notation, part 10

While I have a decent enough concept of the

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 479, Columns 45-62
Source expression
(em "source")

of the data generated through writing in plaintext, I don't yet have a good concept of the

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 479, Columns 154-171
Source expression
(em "target")

of that data. I intend to use datahike as a persistent storage format, but it's daunting to think about where to start.

Here's a good bootstrapping exercise for understanding the format and how it works: a quotes page in Perun. It will read a

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 481, Columns 124-150
Source expression
(in-code "quotes.edn")

file, dump the data parsed out from that quotes file into a

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 481, Columns 211-235
Source expression
(in-code "datahike")

db, and then use that data to generate the

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 481, Columns 279-301
Source expression
(in-code "hiccup")

content for the page. Once that's in place, I'll have a better idea of the schema I need to yank quotes out of the posts where they're quoted and add them to this DB.

One important benefit of using functions rather than markup to define quotes is the ability to

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 483, Columns 96-123
Source expression
(em "preserve context")

by including the references back to the piece of writing containing the quotes. This was one of the great promises of project Xanadu, the ability to see in tandem the multiple layers of context surrounding a link to a passage from another page. I cannot create a system as fully dynamic as Xanadu, but I can use an intermediate evaluation step as text is read from its input format to capture the structure created by the text and its references.

Extensible textual notation, part 11

After a couple of half-hearted attempts to replicate the lozenge syntax of

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 487, Columns 76-98
Source expression
(in-code "pollen")

using a Clojure

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 487, Columns 115-136
Source expression
(in-code "ANTLR")

parser, I discovered the very new but very fully-featured

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 487, Columns 195-280
Source expression
(link "https://github.com/vivid-inc/ash-ra-template" (in-code "ash-ra-template"))

library.

While I recognize that building my own parser is a good programming challenge, I also need to ask myself whether I need to undertake it before doing the work that I want to do in a medium that actually supports it. Right now, I'd prefer the latter.

Extensible textual notation, part 12

The text has extended itself beyond the limitations of Markdown. I now have the power of a real programming language at my disposal in my own writing, and I used it to replace every single backtick and bracket that Markdown required. In so doing, I more fluidly composed the structures provided by HTML by using tools better designed to manipulate them directly instead of burying them under a pre-selected menu of abstractions. I understand HTML better as a result - its structure was not hidden from me. By using a dynamic, computational medium for writing, I can perform Jenny Odell calls "context creation" - I can fully express the context of this page's own creation, false starts, half-baked parsers, and all.

I just wish it hadn't taken until a

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 495, Columns 37-153
Source expression
(link "https://en.wikipedia.org/wiki/2019%E2%80%9320_coronavirus_pandemic" "once-in a generation global crisis")

to get here.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 497, Columns 1-104
Source expression
(header {:level :h3 :date "2020-03-29"}  "Paragraph detection within " (in-code "ash-ra-template"))

Overall, I'm quite pleased with how well

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 499, Columns 42-73
Source expression
(in-code "ash-ra-template")

is working to directly create HTML using the power of Clojure and hiccup only when I need it. Now that I know how to make my own rendering library

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 499, Columns 221-338
Source expression
 [:a {:href "https://github.com/vivid-inc/ash-ra-template/issues/2"} "available to " (in-code "ash-ra-template")]

, I'm seamlessly replacing Markdown's defaults with functions that I have total control over. But there's one thing missing from the experience of using markdown or other plaintext formats - paragraph detection. If I want paragraphs now, I have to insert

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 499, Columns 593-625
Source expression
(in-code "[:p \"content\"]")

blocks, which effectively means I'm

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 499, Columns 662-677
Source expression
(em "just")

writing in hiccup data structures and not plaintext, defeating the purpose of using a templating engine at all.

Luckily, the documentation for

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 501, Columns 32-119
Source expression
(link "https://docs.racket-lang.org/pollen/third-tutorial.html" (in-code "pollen"))

suggests a path forward: post-processing the text after template evaluation to infer paragraph and linebreaks. The documentation for the

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 501, Columns 257-427
Source expression
(link "https://docs.racket-lang.org/pollen/Decode.html#%28def._%28%28lib._pollen%2Fdecode..rkt%29._decode-paragraphs%29%29" (in-code "detect-paragraphs") " function")

also has some useful test cases for paragraph inference on the basis of pre-existing blocks.

Tokenizing the text into paragraphs after evaluation also potentially allows for the recording of the text as facts in a database (see above). I'm not there yet, but I'm about to merge into master and leave Markdown behind for good.

Holotype: further steps towards simplicity

I made good progress generating content with

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 507, Columns 46-77
Source expression
(in-code "ash-ra-template")

- until I wanted to dynamically render an image as part of the build process. I ran into two walls imposed by the closed environment of

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 507, Columns 214-239
Source expression
(in-code "ShimDandy")

: lack of access to the local filesystem, and the Java classes necessary to render images using

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 507, Columns 335-360
Source expression
(in-code "clojure2d")

are not available in that context either.

Fortunately, I recently discovered

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 509, Columns 36-101
Source expression
(link "https://github.com/weavejester/comb" (in-code "comb"))

, a library much like

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 509, Columns 123-154
Source expression
(in-code "ash-ra-template")

by the author of

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 509, Columns 172-194
Source expression
(in-code "hiccup")

, which is pretty much

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 509, Columns 217-235
Source expression
(em "exactly")

what I was thinking about building above, and its parser is exceptionally simple. It lets me seamlessly embed generative artwork into my pages as easily as I can use my templating functions to emphasize text or add headers. You can see an example here.

In addition to that, the fact that the templates are evaluated in the same context as the rest of my code adds the following benefits:

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 512, Columns 1-596
Source expression
[:ul [:li "I can define a map of metadata attributes at the top of the post that is available to the rendering function, so I can set page-level attributes from within each post itself - no kludges like " (link "https://github.com/hashobject/perun/blob/ca090ca77a3aac18b4ff0ac330febb88c26cab84/src/io/perun/yaml.clj" "YAML front matter") " - I handle my posts' data in pure Clojure."] [:li "I no longer need to install my rendering code to my local maven repo to call it from the templates, which lets me" "rebuild all the pages from the REPL, and rebuild the pages " (em "much") " faster"]]

It even uses the same delimiters, which made it trivial to port all my existing pages over to the new library and simplify my project into a single namespace.

(loop (render (eval (create))))

A hylozoic approach to site rendering

I wrote this entry using an assemblage of tools intended to give me real-time feedback on the HTML and CSS generated by my templating functions.

Local development is furnished by

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 521, Columns 35-99
Source expression
(in-code (link "https://github.com/kachayev/nasus" "nasus"))

. Using this simple and lightweight HTTP server, I can launch

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 521, Columns 161-193
Source expression
(in-code "clojure -A:serve")

to fire up a web server on localhost to preview what page changes look like as soon as the render loop finishes.

Originally, I performed batch builds of all files or individual files by passing the names of files to render to

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 524, Columns 114-155
Source expression
(in-code "respatialized.build/-main")

, then refreshed my web browser to get the update via

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 524, Columns 209-230
Source expression
(in-code "nasus")

. But I felt that the latency and context switch of manually rendering files broke my focus. I hope to keep my focus on what I write, not on actively monitoring the contents. I wanted a create-eval-render-loop.

So I rewrote

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 526, Columns 14-49
Source expression
(in-code "respatialized.build")

to support a simple file watch loop that performed a per-page build on any changed file. It felt satisfying to get that feedback so quickly! But it promptly stopped working on the first misplaced parenthesis. I wanted to enable this event loop to recover from failure easily, so I created a rudimentary self-healing mechanism.

Invoking the shell command

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 528, Columns 28-77
Source expression
(in-code "clojure -M -m respatialized.build")

performs a first-pass render, loads all the page contents into an atom, and then uses

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 528, Columns 164-222
Source expression
 (in-code (link "https://github.com/wkf/hawk" "hawk"))

to watch each HTML template file in the source directory for changes. On a file change, the rerender loop triggers. If the template within the changed file renders successfully, the atom gets updated and the new HTML gets written to the target directory. If it hits an error in parsing or evaluating the page content, the atom doesn't get updated, thereby preserving stable state as a fallback mechanism.

Restarting this event loop is a little slow, owing mostly due to Clojure rebuilding the classpath on startup. But there's a way around that. When I needed to redefine some of the library code like

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 530, Columns 198-243
Source expression
 (in-code "respatialized.render/header") 

on the fly, it wasn't a problem.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 530, Columns 277-320
Source expression
 (in-code "respatialized.build/-main") 

also runs trivially in a REPL. All I had to do was switch to that namespace, call

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 530, Columns 403-435
Source expression
(in-code "(future (-main))")

, and I was off. I could extend, rewrite, or add functions from

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 530, Columns 499-537
Source expression
 (in-code "respatialized.render") 

, re-refer the namespace, and use new definitions without restarting the loop.

I have to use it with caution; I accidentally started a recursive succession of multiple file watchers by accident because I forgot to wrap "

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 532, Columns 143-177
Source expression
 (in-code "(future (-main))") 

" in a string. A more defined approach to managing application components could avoid that, but I was surprised by the fecundity of my own creation, even as it slowed my text editor to a crawl.

Growing a website generator

This multi-entry essay has detailed the long and strange history of its own creation. The critiques, gripes, and observations I make here outline my motivation for rejecting existing static website generators, which can largely be summarized as any sufficiently complex static site generator contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of a graph database and query language.

Through

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 538, Columns 9-62
Source expression
(link "/against-metadata.html"  "my experience") 

with infrastructure tooling that stores the definitions of critical systems in a state where consistency is not enforced, composition is impossible, and you have no query capabilities, I realized that my observations about static website generators extended in some respects to the templating tools underlying YAML-based configuration as code.

While these critiques formed the background context of my perpetual rewriting of this website's code (

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 540, Columns 103-210
Source expression
(in-code "pollen -> boot/perun -> ash-ra-template -> comb -> respatialized.parse/respatialized.render")

), in many ways, I did not intentionally design the components that have now become a robust part of ensuring that I generate pages in a consistent format. The validation logic for HTML I currently use is an interesting example of this.

Accidentally backing in to a structure for HTML

If you go back far enough

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 544, Columns 27-178
Source expression
(link "https://github.com/respatialized/respatialized.github.io/commit/9ef441b8a06c66029e5825884c61df19bc5ed0b4" "in the history of this codebase")

, you can find some early failed experiments with Rasmus Anderssen's

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 544, Columns 247-302
Source expression
(in-code (link "https://rsms.me/raster/" "raster"))

CSS grid system. It appealed to me because it specified grid cells explicitly, as HTML elements, rather than implicitly as a presentational CSS rule. The goal for me then, as it is now, is to assign a semantics to document structure, one that captures the idea behind juxtaposing two sections of text with one another. I eventually abandoned

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 544, Columns 645-667
Source expression
(in-code "raster")

for

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 544, Columns 672-696
Source expression
(in-code "tachyons")

, which, despite being CSS rather than HTML, had a "good enough" grid model for presentation. I now realize I was unable to effectively leverage

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 544, Columns 841-863
Source expression
(in-code "raster")

because I didn't know the HTML document model of flow content and phrasing content well enough to see how to fit what I wrote into

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 544, Columns 995-1017
Source expression
(in-code "raster")

's expansion of that model.

I happened to write my

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 546, Columns 24-203
Source expression
(link "https://github.com/respatialized/respatialized.github.io/blob/ce81e6a51d39c843cd91ebe9e3a0cf5999a49a25/src/respatialized/transform.clj" "paragraph detection algorithm")

while attempting to use it for the second time. This meant that I designed the function such that a double linebreak would form a paragraph break

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 546, Columns 350-367
Source expression
(em "within")

a grid cell and a triple linebreak would form a break

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 546, Columns 422-440
Source expression
(em "between")

grid cells. I didn't realize how important the rules of where a paragraph can begin and end would eventually become.

I wanted to identify pathological inputs and corner cases where my paragraph detection algorithm might break down. Instrumenting

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 548, Columns 130-186
Source expression
(in-code "respatialized.document/detect-paragraphs")

with

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 548, Columns 192-226
Source expression
(in-code "clojure.spec.alpha")

for automated generative testing seemed like the best approach in order to get there. However, with the recursive document structures of

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 548, Columns 364-386
Source expression
(in-code "hiccup")

forms, performance rapidly became a limiting factor on input generation.

I already knew about

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 550, Columns 22-43
Source expression
(in-code "malli")

, a library to express specs as pure EDN that focused very heavily on high-performance use cases. However, when I had resumed work on

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 550, Columns 177-199
Source expression
(in-code "raster")

forms and paragraph detection for them, the initial implementation of sequence expressions had not yet been merged into the codebase, which made validation of nested

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 550, Columns 366-388
Source expression
(in-code "hiccup")

forms a non-starter. I instead chose to use

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 550, Columns 433-460
Source expression
(in-code "minimallist")

, which had a working implementation of sequence expressions and

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 550, Columns 525-629
Source expression
(link "https://github.com/green-coder/minimallist" [:span "recursive " (in-code "hiccup") " forms"])

that I gradually refined into

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 550, Columns 660-822
Source expression
(link "https://github.com/respatialized/respatialized.github.io/blob/04321e606620b452230bef7b0b1925c140f51a45/src/respatialized/document.clj#L215" "a schema")

for non-interactive HTML forms by following the

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 550, Columns 871-958
Source expression
(link "https://developer.mozilla.org/en-US/docs/Web/HTML/Element" "MDN HTML spec" )

. While the

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 550, Columns 970-997
Source expression
(in-code "minimallist")

design document says performance isn't a primary goal, it was more than performant enough for some simple generative tests of the paragraph detection algorithm.

By the time I had refined this work enough to re-render my existing pages,

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 552, Columns 76-166
Source expression
(link "https://github.com/metosin/malli/tree/0.3.0" "sequence expressions had landed")

in

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 552, Columns 170-191
Source expression
(in-code "malli")

. With one iteration of the HTML spec under my belt, and with corresponding tests for form validity, I re-translated my work from one spec library to another and expanded it to encapsulate the root elements of HTML.

Without even indending to design it at the outset, I now had a model for HTML expressive enough to reuse within my paragraph detection algorithm that could process forms differently depending on the surrounding context - paragraphs cannot be contained within paragraphs in HTML. Before long, I had a test suite that

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 554, Columns 317-500
Source expression
 (link "https://github.com/respatialized/respatialized.github.io/blob/52b4c9f78f28f8e77b8f5828eb66555fdc9d0bd0/test/respatialized/document_test.clj#L527" "continuously conformed")

all of my existing pages to the top-level model of HTML. I had assigned a semantic structure to the forms of the document that I wanted to process, one that captures the hierarchy of HTML forms far better than anything I could have designed if I had tried to design something from scratch.

None of the validation logic that now exists would work without the efforts of Vincent Cantin, as well as Tommi Reiman, Pauli Jaakkola and the rest of the contributors to

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 556, Columns 172-193
Source expression
(in-code "malli")

. I no longer have to shy away from the complexity of HTML - I now have a spec expressive enough to give me the leverage I need over it.

Fabricate


Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: blockquote in this context
Error phase
:compile-syntax-check
Location
Lines 561-562, Columns 1-141
Source expression
 (blockquote {:author "Matthew Butterick" :url "https://docs.racket-lang.org/pollen/"}
               [:em "...if you can find a better digital-publishing tool, use that. But I’m never going back to the way I used to work."])

I have simplified the code that generates this website to the point where I can successfully extract it into its own static site generation library, called

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 564, Columns 157-225
Source expression
(link "https://github.com/fabricate-site/fabricate" "fabricate")

. This

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 564, Columns 232-315
Source expression
(link "https://github.com/respatialized/respatialized.github.io" "source repo")

now consumes that code instead of defining its generation process itself. The

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: in-code in this context
Error phase
:compile-syntax-check
Location
Line 564, Columns 394-470
Source expression
(in-code (link "https://www.dictionary.com/browse/holotype" "holotype"))

has become a prototype.

Using the

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 566, Columns 11-95
Source expression
(link "https://fabricate.site/finite-schema-machines.html" "organizing concept")

of finite-state machines, I have developed an extensible method of defining the steps necessary to create a collection of HTML pages.

You can read more about the intent and point of view informing Fabricate

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: link in this context
Error phase
:compile-syntax-check
Location
Line 568, Columns 74-127
Source expression
(link "https://fabricate.site/fabricate/" "here")

. I hope it brings the power and flexibility of Pollen's publishing system to the Clojure ecosystem. I also hope it means that I can write about things apart from the website generation process on

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
Unable to resolve symbol: em in this context
Error phase
:compile-syntax-check
Location
Line 568, Columns 324-339
Source expression
(em "this")

site.