An example from The Tinderbox way

While reading an outstanding «The Tinderbox way» I try to learn TB functions and reproduce codes from the book.

On page 300 Mr. Bernstein analyzes lookup tables using. The point of the example is to find the abbreviation of the book in the name of the note in another container and take the full title and author’s name from there.
Unfortunately, I can’t reproduce the result from that page.

I try to write down all the steps, inserting “?” where I am not sure of the correctness of my actions:

  1. We create two containers: «References» and «Notes». The former is for

  2. Inside Reference container we create a sample note with JHF as a title (abbreviation of the author’s name).

  3. For created note we assign two prototypes $BookTitle and $Author and fill them according to the content.

  4. We create a Prototype for notes

  5. We set an OnAdd Attribute for Notes container:
    OnAdd:$Prototype="Reading Note"

? 6) Then we set a Rule for the container (or for notes inside the container?)
find( inside(/References) & $Name==$Source(that) )

I don’t understands what this rule does.

Then Mr. Bernstein suggests to use another rule:
? 6) We set a rule for Notes container (?)

$MyList|=find( inside(/References) & $Name==$Source(that) ); 
$Author|=$Author($MyList.at(())) 
$BookTitle|=$BookTitle($MyList.at(()))

I understand the syntax but I can’t get it to work.

Could you help me figure it out?

The key is the designator ‘that’ (described here). In plain English, insofar as is possible the two query terms mean:

find all notes (or aliases of notes) that are direct children of the container at the path ‘/Reference’

the title ($Name) of a tested note/alias exactly matches the value of $Source in the note calling the find

As the two terms us an AND query join (i.e. the ‘&’ character ) to match, both terms must evaluate true. As a side note, only notes matching the first query are tested for the second. Why? Because both must evaluate to true for if the first evaluates false the outcome of the second test would be irrelevant.

The reason for the that designator is it is the only way you can tell the query the you want to access an attribute value outside the find() and specifically from the note running the query. If you understand the ‘agent’ designator’s behaviour, this is an analogous scenario.

I only have and early proofing draft of the book (page numbers differ) but it has slightly different code for the rule:

$MyList|=find( inside(/References) & $Name==$Source(that) );
$Author|=$Author($MyList.at(0))
$BookTitle|=$BookTitle($MyList.at(0))

(Note how your version lacks the value in the .at() calls)

This version makes more sense but has a typo that is likely causing the typo. The rule contains 3 expressions (discrete actions) as listed one per line. But if an expression is followed by another within an overall action (e.g. here, a rule) it must have a ; semicolon terminator. A source code line break is ignored by the code parser. The last expression does not require a terminator, but it’s a good habit to acquire. Professional coders resent extra keystrokes, but for the rest of use for whom software function is near-magical, consistency helps. So, cleaned up, we get:

$MyList|=find( inside(/References) & $Name==$Source(that) );
$Author|=$Author($MyList.at(0));
$BookTitle|=$BookTitle($MyList.at(0));

Sure enough, if I run my first example without the semicolon, I get an author name but no book title. We the semi-colon, I get both:

In my test, I added $MyList to the ‘Reading Note’ prototype’s Displayed Attributes so we can check the find() works.

Here, you source is ‘JHF’, and the rule results in a single-item list (as in the grab above). So if $MyList holds /References/JHF then $Author($MyList.at(0)) expands to:

$Author("/References/JHF".at(0))
i.e.
$Author("/References/JHF")
which has a value “Franklin, John Hope”. Same proves applies for the book title.

So two problems detected:

  1. An error in the book’s code (missing semi-colon(s). NOTE: this assumes my early proof version’s code wasn’t corrected before publication (error #2 below suggests it was not corrected)
  2. Copy/paste, or transcription, error by you turning a 0 in ().

Here is may test doc for the above:
book-ref.tbx (94.6 KB)

Note: I added a colour and badge to the prototypes just to make it clearer where they were used. Also added $MyList as a Displayed Attributes for reasons above. As I don’t have the book in question I can’t be 100% certain I’ve followed process, but I think I answered your problem :slight_smile:

†. (Having read further) In fairness, the book’s copy does explain what 'that means.

2 Likes

Thank you very much for such a detailed answer, Mark!

I misread the code: mistook zero for the bracket signs.
Now everything works!

Yes, I understood that. At first I thought that
find( inside(/References) & $Name==$Source(that) )
is a separate rule and as I understand the code I still couldn’t get what it should do.
Now it finally dawned on me that this is just a fragment of the rule. :slight_smile:

One aspect that remains unclear - is .at(0)
($MyList.at(0))
As far as I understand usually a specific attribute is set after .at.
For instance:
$Price = $Text(/Price List).at($Name)

And in our case there is zero instead of attribute. Could you clarify that?

Once again thank you so much for your help!

Really? Not as far as I’m aware.

(Publicly) known use of .at() is as documented here. As far as I know .at() only takes a number as the input argument. Of course, if $Name is a number then both work.

Than again, Tinderbox has had long and gradual formalisation of forms with read to look-up lists, ending in the Dictionary data type. Long time users (me!), like the slowly boiled frog, can miss such shifts.

Anyway, if I’ve misunderstood, ask again.

Actually, [dictionary].at(key) does work, and was adopted as an extension of this early usage in lookup tables!

Nowadays, we’d probably use $Text(/Price List)[$Name], but .at() is fine, too.

1 Like

I think this relates to the now deprecated use is `at() with look-up keys. See the second, but deprecated, syntax form of list.at()

The no-foul takeaway here is to reimagine the opening example in a Dictionary context.

1 Like

Busy recent period so my apologies to @Maximus and others if my last was terse. A problem with a long lived (21+ years…) app and constantly evolving action code is things change.

Since the code examples cited, Tinderbox has added the Dictionary-type which further formalises the [ ] argument wrapper. So we have:

  • List/Set.at(N), where N is a zero-based item number in the source listing.
  • List/Set[N], where N is a zero-based item number in the source listing.
  • Dictionary[“key”], where the Dictionary value of the key, where key is a literal string or the string value of $SomeAttribute supplied as key

As $Text is neither a Dictionary, nor List nor Set type, this is legacy coding to be avoided. The presumption is $Text stores what amounts to a list allowing a form of List.lookup() usage. But this is also now deprecated in favour of Dictionary type data (see Dictionary["key"] above).

None of this is to be unkind to the very useful “A Tinderbox Way” book that many find useful. It’s simply that print can’t easily change once on the printed page. In aTbRef I have tried(!) to point from legacy/deprecated forms to their current equivalent but am always happy get news of (and so fix) any holes in that.

1 Like

Thank you, Mark! Your addition and clarification you’ve made are very useful.
“A Tinderbox way” is interesting and inspiring but some codes changed since the last edition of the book. Anyway, it is valuable and well-written.

1 Like