This Website 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.
Type | Disadvantages | Examples | Advantages |
---|---|---|---|
Binder notebook | atemporal,apresentist | Filofax | associative,organic,frictional,multi-modal,simple |
Diary | apresentist | Bullet journal | chronological,frictional,reflective,multi-modal,simple |
To-do app | decontextualized | Nozbe, todoist | fast,simple,portable |
Kanban | decontextualized,information-poor | Trello | situated,simple |
free-form/wiki | hierarchical,laborious | Notion | associative,compositional,iterative |
Website | laborious,atemporal | This | frictional,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:
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.")
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.