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. 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 2.

Listing 1

Tagged phrases are available through 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 name of the 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 exammple1 is an arrow funciton. Whenever a function is encountered during text generation, the function is called with the tags collection as its argument. This 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. This does not cause a problem, 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. In this instance .data.value and .data.animal are equivalent. This is due to how .populate() works. If the incoming data object does not explicitly have a .value property, it will be created and set to the first property of the incoming data object.

The pet tag for example2 references the pick phrase with the indefinite article prepended. If we want the bare pick phrase, we can use the .inner property of the pet tag, 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


Some prefixes expose additional metadata. Phrase definitions that include .cycle, .favor, .first, .last, .pick, .roll, and .series include .index and .total as properties of .data. The .index property is zero based. Listing 2 shows examples of using metadata.

Listing 2

The .series property requires some special handling when accessing metadata. Once the entries 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 not existent properties will cause an error. Therefore, we can only access the metadata 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 to those that match the person's intersest, we add the .concur suffix to the hobby sub-phrase. The rule function returns true if the hobby matches the persons 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 target 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.