Text generation Part Three

Prerequisites: You should have read Text Generation Part Two.

The first two parts of this tutorial covered basic text generation. We are now ready create some more advanced phrases.


The .tag() suffix allows us to attach a name to a phrase. We first encountered tags in part one, where they were used to tag phrases for data population. However, there are other uses for tagged phrases as well. Sub-phrases that are tagged with a name can be reused in a containing phrase and also provides access to additional properties for the sub-phrase. Examine the JavaScript in listing 1 below.

Listing 1

Tagged phrases are available for reuse via the .tags prefix.

The first sub-phrase of example1 is tagged as pet In the second sub-phrase, _.a.tags.pet is used to refer to the sub-phrase tagged as pet. The .tags prefix provides access to the collection of tagged phrases and it must be followed by the tag name associated with sub-phrase. The .a prefix adds the appropriate indefinite article to the sub-phrase. During text generation a new (and different) animal will be picked at random for each of the two sub-phrases.

Sometimes when referring to a tagged phrase, we don't want to generate entirely new text. We just want repeat the same text that was already generated in an earlier sub-phrase. The third expression in example1 is an arrow function. Whenever a function is encountered during text generation, the function is called with the tags collection as its argument, which gives the function access to the tagged phrases. In this case the text property of the pet sub-phrase is returned.

Study example2. The pet tag refers to the whole phrase, _.a.pick(), which first picks an item and then adds an indefinite article. When the pet phrase is populated, the data needs to go to the inner .pick phrase, not the outer .a phrase, but the tag refers to the phrase as a whole with both prefixes applied. This is not a cause for concern, however, because .populate() will always identify the innermost (terminal) phrase of a tagged phrase and deposit the data there.

We can reference the data associated with the phrase by using the .data property. Here, tags=>pet.data.problem returns the problem associated with the selected pet and tags=>tags.pet.data.value returns the primary data associated with the pet phrase, in this case the selected animal with an indefinite article prepended. If .pick selects an octopus then .data.value contains an octopus, but .data.animal contains the unaltered data, octopus. At the time of data population, if the incoming data object does not explicitly have a .value property, it will be created for the phrase and set to the first property of the incoming data object.

We can also reference the inner .pick phrase of the pet phrase in example2 by using the inner suffix: tags=>tags.pet.inner.data.value. We can chain as many .inner properties as needed to reach a deeply nested phrase.

Alternatively, we can use parentheses to nest phrases and create tags at different levels of nesting. This is shown in example3

Cacheing data

Data population for standard tagged phrases expects the nesting of the sub-phrases to align with the structure of the incoming data. Recall that when .populate is called with an Object as an argument, the property keys of the Object are matched against the ID tags of the sub-phrases and the data contained in each matching property of the Object is sent to populate the corresponding sub-phrase. If the sub-phrase has its own tagged sub-phrases, the data object is further destructured and the corresponding sub-properties are applied to the sub-sub-phrases. This process of destructuring and applying continues to the innermost depths of the phrase.

With standard tags, if the data you are working with has a structure that doesn't align very well the phrase's structure, the data will not be matched properly. Of course, you can remap the data to match the phrase, but sometimes it's easier to give the phrase the responsibility of parceling out the data to the sub-phrases. This is done by creating a cache tag for the phrase. Like standard tags, cache tags provide a means of referring to data associated with the phrase, but unlike standard tags, the data is not automatically propagated. Instead, arrow functions are used in the phrase's expressions to extract the phrase's data.

Refer to example4 above. The data, {available:{animal:"cat"}}, assigned to the phrase via .populate, is accessed through the pet tag using arrow functions.


Some prefixes expose additional metadata. The .index and .total metadata are available for .cycle, .favor, .first, .last, .pick, .roll, and .series. The .total property is the length of a phrase's array. The .index property is the index number of the selected sub-phrase from the phrase's array and is zero based. Listing 2 shows examples of using metadata.

Listing 3

The .series property requires some special handling when accessing metadata. Once the items in the series are depleted, the series returns an empty array for the results and there are no items of metadata to access. Attempting to access non-existent properties will cause an error. Therefore, we can only access the metadata for .series under certain conditions. Examine example2 in the listing above. The .also suffix signals the start of another ishml.Phrase that will only be evaluated and appended if the preceding phrase returned at least one result. If the preceding series is depleted, the phrase associated with .also will be skipped. Like .then, it is acceptable to chain prefixes after .also.


The .per suffix repeats a phrase for as many entries as there are in the referenced tagged phrase. Use it whenever you want to iterated through the array of sub-phrases. See listing 3 below.

Listing 3


The .concur suffix filters a set of sub-phrases so that it conforms to the criteria of a provided rule. See listing 4 below.

Listing 4

The example1 phrase picks a hobby for each person based on their interest. To filter the possible hobbies by those that match the person's interest, we add the .concur suffix to the hobby sub-phrase. The rule function returns true if the hobby matches the person's interest or false if it does not. This function receives the tags collection as its first parameter and the result of each sub-phrase evalution of the targeted phrase as the second parameter. Sub-phrases that do not meet the criteria are removed from consideration before .pick is applied.

Continue to part four to learn about recursive phrases.