Tinderbox Forum

Tinderbox Meetup: 20 March 2021

Will you post the link later? It seems to be missing.

Thanks for a great meeting, all! I neglected to pull the chat transcript while leaving; if anyone could PM the text file to me, that would be greatly appreciated.

Hi all, sorry for having the call run long today…was having way too much fun. :slight_smile:


Amen, and sorry for being late to the Pass.

A point I took from Art (? IIRC) was the generic arcane ‘rules’ of code. Who cares is if use { or [? Well, the computer does. This is story the tech communities have perhaps overlooked in favour of religious wars over emacs vs. vim.

I do see the [optional] ins not obvious or that while " and can easily be created by typing the same keystroke, the software doesn’t see that semantic gloss and sees only different character keycodes.

Back to the ‘two tribes’ and being ‘tall enough to go on the ride’. (Unintentionally) exclusionary in first language let alone the larger cohort having to re-translate it mentally to their ‘real’ first language.

I do feel a pressure to do better in this regard, even if not sure of the next step. We’re not trying to train programmers but rather we do need to understnad that for most folk ‘code’ is more like learning русский or ᐃᓄᒃᑎᑐᑦ and accept that ‘code’ is not blindingly intuitive to the user. We can and must do better!


Ok, I’m confused as the 6 Feb video link on the listing page points to https://vimeo.com/509289464. Or, am I missing something?

As the listing says - you had to be there. No one pressed ‘record’. :open_mouth:

1 Like

More accurately, the software does. More specifically, the parser implemented by the software does. And really, its the grammar for the language that the parser recognizes. Delimiters link “{” or “[” reflect arbitrary language design decisions. Open and closes curly braces could just as easily be replaced by “begin” and “end” as some languages do (e.g., Pascal and Ada). In more recent cases, block structure is delimited by indentation (e.g., Python). And some languages recognize a conditional construct (E.g., if) signifying the beginning of a block and use a corresponding token for ending block (e.g., “end-if” or “fi” in Bash and Zsh). The point is that the language syntax is explicitly chosen by the author or the language’s grammar (Mark Bernstein in the case of Tinderbox). Sorry for being pedantic, but if we’re going to discuss these topics, then it’s best to understand the terminology and the reasoning.

We’re not trying to train programmers but rather we do need to understnad that for most folk ‘code’ is more like learning русский or ᐃᓄᒃᑎᑐᑦ and accept that ‘code’ is not blindingly intuitive to the user. We can and must do better

As observation that I made when I taught Computer Science is that beginners — particular those not majoring in STEM — suffered from the problem of not being used to having to formalize their thinking to the degree necessary to write a program. Some people never get it (most eventually do) and move on to something else. if you can explain to someone how to tie their shoe laces, then you can probably learn to program. To me, the challenge for Tinderbox is getting those folks that are not used to thinking formally over the hump. I know from my teaching experience that the path is not easy for everyone, but I have found that most can make it with the right kind of hand-holding.

Yes, Tinderbox is a tool for notes, but it is also about a specific kind of programming (scripting, if you like), and that programming is integral to success.

1 Like

Don’t disagree, and I’m not discounting anyone’s trade. I’m just reflecting on the gulf so many see in getting started. so many of these character differences make sense only after the fact. We need a better starting narrative for those who have no coding background. It’s less a matter of who sets the rules but understanding why the rules exist. Spoken and written human language is much more flexible than code (even allowing of the grammar pedants). We need better metaphors to map the coders’ view to a wider wider audience.

I don’t pretend to know the answers, but I’d like to help break down barriers to using (coding) affordances that are fast becoming part of the 21C digital existence. I’m constantly reminded by what I know now that was unguessable before I learned it as no-one felt it necessary to find bridging metaphors.

This is a hard task, one is doing something for which there is zero reward, yet if done well it enables so many more to use what are still niche skills. Experts will still be expert, so there is no loss in this advance, but a general societal gain.


Apologies - I didn’t (know I had to) check the Meetups/Logs thread as well.

No ‘had to’ :), it’s just the master record—or the one I try to update.

I agree, and am more than happy to help where I can.

1 Like

I believe that for me, a lack of knowledge regarding the rules of syntax and terminology within the realm of action code scripting is the key limiting factor.

Some things I can pick up, others I may require - and can build glossaries for - for eg., whether I should use a ‘*’ or a ‘?’ for wildcard characters, or whether the ‘!’ was used for factorials or to exclude a variable, or maybe for something else altogether.

Other scenarios are a little fuzzier. Take for example the following passage from the excellent and inspiring “The Tinderbox Way”.

Pg 215, edition 3

Here’s how to understand even complex actions like the one we just discussed.

While someone with scripting experience would parse the above instinctively and momentarily, someone like me can tell only the following -

  1. OK, this is a line of code to set the $Subtitle attribute of one or more notes.

  2. That variable will be a $Name value.
    Of the same note? or yet another different note? <open_question>
    Hm, ok maybe it’s another note that will be found in a sub-container of a container named “draft”; to this will probably be appended a value of the note’s $Modified attribute, perhaps that’s the meaning of the “.”. <a guess; hold on to the thought and see if anything further here supports or negates it>
    But then - what are we sorting? Is $Modified a set or a list? If so, on what conditions are we sorting and picking a value from the set/list? No, that can’t be right
    Maybe we’re supposed to sort something else and then set the value of the $Modified attribute accordingly. Or maybe it’s something completely different and I’m completely missing the point.

  3. And beyond that, there are questions about things like brackets, and periods, and the presence of (-1)…
    "huh? brackets? why brackets here? I haven’t seen any lines of code until this page that deploys brackets. Did I miss a page along the way where brackets were introduced? Probably. Let me skim back a few pages/chapters to see where else brackets are being used, with perhaps an explanation
    Hm, I don’t see anything I understand. Now, where would I go to find out when I need to use a bracket? google? the forum?
    Is the bracket referring to action code, or is it just a way of delineating a path to some instruction?
    Is ‘descendedFrom’ a standard instruction used by all programmers, or is it a term specific to Tinderbox? If the former, where do I go to read a /man instruction regarding its syntax and usage? If the latter, where can I find a glossary of other similar terms I might leverage and deploy in my Tinderbox scripts? OK let me check atbref - wonderful, it says that “descendedFrom” returns a boolean value depending on whether an item <hmm - does item mean Note? Or an $Attribute currently being searched? Let’s find out once I understand it in context of the command above>. OK nevermind, I can’t actually pull the whole phrase together. Help!!
    And - why is this code packet typed out in this specific way? What is .at, and why is it preceded by a period? Why is it on a different line from the previous instruction? And so on…

  4. Now in this particular case, @eastgate goes to great length to explain and parse the instructions, thankfully. But even so, I have difficulty following - for example, it’s not obvious to me that $Name(find(descendedFrom(/draft)) means to pull the $Name value of the note from ANOTHER note. I initially thought the $Subtitle was simply the $Name of that very note, followed by some find operation and sort operation and some .at operation. And no idea why “find” and “descendedFrom” are separated by a bracket.

So - although it’s a little long-winded, I think the above clarifies the kind of existential anxiety non-script-savvy folk like myself are faced with whenever we peek at an Agent or Rule window… it seems silly, but my missing knowledge gap is just wide enough to make any Indy Jones-style leap-across-chasm a leap of 99% faith, and 1% understanding. Again, I’m delving freely into hyperbole, but only to illustrate the resultant anxiety/confusion.

So - I think that what I seek is a learning resource - a guide to speed my general comprehension of terms, syntax, and usage. Some way to correctly identify between commands, placeholders, and generic regex terms. It seems that should be my first step toward allowing the action code language to emerge in all its logical elegance. Or maybe I’m barking up a completely unrelated tree.


This will be a long answer but stick with it…

(BTW the following is on paper page number 215 of The Tinderbox Way v3, but is on digital page 224 of the PDF.

In the book, @eastgate helpfully splits the code onto lines - to help the reader parse the (sequential) parts:


And, in fairness the text that follows on pages 215–16 does break down the meaning if the syntax. But, let’s try again. Note: the syntax colouring is applied by the forum and doesn’t follow Tinderbox’s methods, but that’s a whole different deal. Meanwhile, we can decompose the above code even more, in a different way, to add clarity for discussion:


You ask:

Not same, but a different note. Why? The basic form of setting an attribute value works like this:

$AnAttribute = "some value";
$AnotherAttribute = 4;
$YetAnotherAttribute = date("today");
...etc., depending on the varying types of data being used

But often we want to set a value already stored in an attribute. Thus:

$AnAttribute = $DifferentAttribute;

always sets attribute AnAttribute to the value of attribute DifferentAttribute of the current note unless we tell Tinderbox to look elsewhere, which involves parentheses attached to/following the right-side attribute. Thus by using another note’s title in quotes ($Name):

$AnAttribute = $DifferentAttribute("Some Note");

or its unquoted full path ($Path):

$AnAttribute = $DifferentAttribute(/A container/Some Note);

Both do the same. Indeed you can do the same on the other side or both (unusual but the need could arise:

$AnAttribute(/A container/Some Note) = $DifferentAttribute;
$AnAttribute("Another Note") = $DifferentAttribute"Some Note");

The first, title-based form, is shorter and so generally most people’s preferred usage. So why would you need the path-based form? Well, what if you have two notes both called “Some Note”?Tinderbox happily allows this, as the note’s title is just an attribute (Name) and the the unique identifier is actually the ID (again … an attribute) which is a long numerical number; you don’t need to bother about the latter, it just ensures note A and note B had different IDs. The path-based way of referring to a note allows closer-grained specification of the intended target note. Otherwise, if faced with a choice, Tinderbox will always choose the match with the lowest ‘OutlineOrder’ attribute value.

At this point if we don’t know what descendedFrom does, in terms of outcome. So, rather than guess we can look it up. We can use the app Help or look online at aTbRef. The latter has a set of links top/bottom of every page with links to listings of major code related things like export of action code.

As descendedFrom is action code, the ‘Action Codes’ link looks like a good bet. That shows us an alphabetical list of actions and sure enough descendedFrom(item) is listed. We then find that:

…it matches all descendants of item however deep the outline branch beneath it

So, descendedFrom(/draft) is looking for notes descending from the note draft.

We are sorting the output of the previous operator. Chained dot operators, work in a chain (goring in succession from left to right—thus the term. Resolve the first, apply the next, and so on.

By contrast, if each part of the above sequence of action were a non-chained function we would end end up with nesting looking like this (N.B. the following is not valid Tinderbox code):


Now where do you start trying to make sense of things. Actually in the middle - in the innermost part, as you would with an Excel expression. But, chained operators are actually easier to read once you get the hang of them. As dot expressions didn’t arrive until Tinderbox v4.6, you may well see two forms of some functions, e.g. format() and .format() and these are generally interchangeable. Indeed, older code samples you may see around likely use the non-dot-form but can easily be translated into the dot-operator form.

Why does this matter? Both are a form of multi-value listing and once wrote the result, as passed elsewhere is the same. The sort doesn’t alter the original, it sorts output based in the original. Otherwise, the .sort() would move notes around the outline that matched the find(). The .sort() doesn’t pick anything from the list, it simply sorts the order of the list that it is passed by the preceding operator

Why use the input $Modified and not "Modified", i.e. an attribute value reference rather than simply the name of the attribute on which we want to sort? Well, we’re telling the app we want to sort a list but using a different list of value, i.e. the list of values of the attribute named"Modified" (ergo, $Modified) but only for the items in the list passed to .sort().

Finally, it is the .at() operator that picks a single item from the list. If we look at the documentation we see .at() uses a number running from 0 (zero) for the first character, up to the length of the list and with the extra affordance of minus numbers (starting a -1) numbering from the end backwards.

So, we are getting the last item in a list sorted on date (which sorts chronologically), and thus the most recently modified item. Those items are all the notes descending (in outline terms) from the note ‘draft’. Furthermore, at this is quotes as starts with a /, we know ‘drafts’ is a container note at the top level of the outline. So we get a single descendant of ‘drafts’ which is that which was the last to be modified and it is that item which is the note whose $Name is passed to set the value of $Subtitle, and so create the current note’s subtitle in map view (only map view displays a subtitle in the view pane).

Is that true though? If I look at the TB-Way’s chapter 12 (paper page 200 onwards), I see lots of use of parentheses, e.g. bottom of paper page 201. So, that critique seems slightly unfair. Still, why parentheses are used at all is a fair question and one I do think merits better answer, though understanding/intuition is very variable amongst readers and its hard to write for everyone’s foibles. Pick any good widely-used teaching book and you’ll find a few students who just don’t get the presented info but happily use a different author’s book on the same subject.

If one is just guessing by putting in things like parentheses when unsure, rather than following documentation, I’d suggest that’s not a good approach. If the documentation says use operatorName(input1, "input2) then do so: provide two inputs in parentheses, the first without quotes and the second with quotes. Or certainly do so at least until you’re comfortable enough to know where you can colour outside the lines. That’s certainly why aTbRef is more precise than the Help because when ‘code’ all seems like a set of magic incantations, simple rules help one safely get going.

Google? Not great as Tinderbox is a smaller user community than, for example, Python users or people writing HTML, etc. You may find something in Google but most likely it will be pointer to Eastgate’s website or aTbRef (I suggest you try both those resources first).

The forum? Yes, it is exactly the sort of thing to bring here, e.g. “how do I find the most recently modified descendant of a particular note?”. I’d like to think this forum is both responsive (in terms of getting an answer quite quickly—even if a direction to another resource) and friendly in tone. We can’t account for people’s experience in other forums: experiences there may vary!

I’d politely counter that does that matter, unless you understand what such a difference implies? It’s not that it is a silly question, but more that it doesn’t help you get to grips with the code. Tinderbox action code does include a lot of features common to general programming/coding. However, the names of operators and syntax for their use vary massively between coding languages so the value of the above question, whilst well intended is—in reality—moot.

But wait, that same article has a link titled “Ways to define item” which I’m guessing you didn’t follow. If you do, it takes you to a note " Notes as ‘item’ objects in action code" which goes through some detail how ‘item’ might be defined as an input.

But, we might ask, “Why not put that in the article on descendedFrom() so I don’t have to waste my time following links and can read it all in one place?” Well, over the 14 years I’ve documented Tinderbox, action code has grown significantly both in scope and complexity and so the meaning of ‘item’ has changed too. There are now 211 operators listed and it makes much more sense to write this ground truth once and link to it rather than update 100s of documents each time (pity the poor author!). Plus, once you’ve learned about item, you don’t have to skip though lots of now known stuff and only keep an eye out for exceptions. Also, the whole concept of hypertext is the user follows links: you go to the content you want rather than software guessing what you want to see.

That sort of suggests this would be clearer:


I’m less sure. :slight_smile: For all the hoopla about AI, it still sucks at understanding human-written text. Plus, most apps aren’t even using AI. So we need to signal our intention in what may seem a rather pedestrian manner. But think of going to another country that speaks a language you don’t know and using a written script you can’t read. You need a guide book (documentation) and—until—you can speak the language like a native, to stick to standard constructs and simple instructions.

Going back to the starting code, we want the title of a note, but not the current note, and which we can only define via a query. We could use an agent, indeed, as described on p215 of The Tinderbox Way. But, to do this in an action we need to use the find operator to handle the querying task, enclosing the the query in parentheses, e.g. find(query), to indicate to the app that the query is an input to the code the app runs in response to a find request.

In summary, I think the info is there (though I grant it can be improved). The issue is how one engages with it—and that’s not criticism. As noted above, computers aren’t smart enough to understand us (ever tried asking Siri, Cortana, etc. to do anything non-trivial?). So, we have to signal our intent to them in terms they can understand unambiguously. We humans are, of course never ambiguous in our statements. :open_mouth:

I’d try to avoid treating this issue of understanding Tinderbox action code as a binary case of understanding all or nothing. Insight takes a few iterative steps, each new revelation underpinning a wider understanding and eventually the ability to guess where things may may not work.

As an endnote, when looking at the listings of operators and attributes, don’t over look the links in the Displayed Attributes-like tables at the head of the page, e.g.

Lastly, actionable suggestions for additions/improvements to aTbRef are always welcome as is information on typos, errors, out-dated info, etc. There are many notes in the ressource and I’m a really poor typist!

†. Disclaimer. I’m the sole author/originator of aTbRef

‡. Bear in mind that Eastgate doesn’t have a documentation department and aTbRef is written in my spare time (whilst also running this and the Backstage forum). The fact that I’m just a (licence-paying) user—and beta tester—means I’m able to document things Eastgate is still working on right up to release point allowing the resource to include things that may slip from the app Help. TL;DR the resource for documentation is small…and finite!).

[Edits: for typos/better sense]


Incredible!! I actually can feel clouds lift as I read through this.

And of course, your patient response solidifies the claim that (almost) no question is treated as stupid on this forum, and all shall receive illumination. Thanks again @mwra!!

I shall read and re-read this and use the things I pick up in my comprehension of action code in other examples. Things like

< quote >

$AnAttribute = $DifferentAttribute;

always sets attribute AnAttribute to the value of attribute DifferentAttribute of the current note unless we tell Tinderbox to look elsewhere, which involves parentheses attached to/following the right-side attribute"

< / endquote>

I simply did not know, and this knowledge will now incrementally improve my ability to parse all similar lines of code!


  • The specific references to the example in the Tinderbox Way and atbref were ONLY pulled in for example. I readily agree that brackets were introduced earlier than pg 216. And I certainly should have kept an atbref tab open even while reading the Way. And specifically - both of my examples above WERE explained in the following sections. I was simply isolating the conditions leading to my anxiety and general feeling of helplessness, and my suspicion that my ignorance of conventions that are rather standard for more code-advanced minds than mine are not only a limiting factor, but actually increase the amount of work my brain has to do in order to guess my way through what in an hour or so I will be able to read as plainly as “The rain in Spain stays mainly in the plain.” :smiley:

Thanks again SO much!!


You are a writer. :slight_smile: Love this.


Mark, is this a typo? What is .at90? What if there are 15 items in the list, would it be .at[15]?

Here is my take on this. As Mark points out, rather than guess, follow the documentation. Once you get it to work, you’re good! Overtime, if you want to circle back and understand how it works, go for it. At the start, I find it important to first get the result I want, the how, and the figure out the why.

There is a scene in the 1986 move The Fly that I remember vividly. The main character Seth Brundle explains that he does now how all the parts of the the computer works, but he does now how to put them together to build his transporter. I reflect on this often. Know enough to get the job done and then when you can, dig deeper to build more expertise. At least that is my take.

Yes it is. We share an affinity for typos. :rofl: Also, I’d just fixed that. I find the rendered article much easier to proof that the raw Markdown. <sigh>.

1 Like

Here is a really good one for you to keep in mind = vs. ==.

When you’re running action code there you can use “=” and “==” arguments.

You use “=” when you want to assign the value of the argument on the right side of the equation to the attribute on the left side of the equation. For example, $Text=“Hello World.” would make the value of the note’s text “Hello World.” Or $Name=$Name+" “($Created.format(“l”))”, would change the name of the note to EXISTING NAME (DATE). IOW, if the note’s name was “This Note” and you created it on 3/14/21, after applying this action code (via a Stamp, Rule, Edict, OnAdd or Link Action) the note’s name would be transformed to “This Note (3/14/21)”.

As for the double equal “==” this is a logical test, e.g. does the left side of the equation equal the value of the right side. For example, let’s say you want to run a query but you only want to return aliases. You could say $IsAlias==true. Or, if you wanted to only get the notes that were assigned with a specific prototype you could say $Prototype==“Person”, which in an agent would only return those notes with the prototype person. If you wanted to combine the two arguments you could, e.g. $Prototype==“Person”$IsAlias==true. This would return all the notes that are assigned to the person prototype and that are aliases. If you did not want the aliases, instead of == you’d use !=, which means is not, e.g. $IsAlias!=true. Note, you don’t need to quote true or false, but all other equality arguments you do need to quote. For instance, $HeadingDepth==“1” would be the correct way to test if the value of the $HeadingDepth attribute for a given note was equal to 1.

1 Like