Accumulator

As noted in the overview, one uses an accumulator to collect information about sequentially numbered sections, cross-references, tables of content, etc., then uses a second accumulator to render the source text with these features. Accumulators are made of up of reducers and folds:

type alias Reducer a b : a -> b -> b
List.foldl : (a -> b -> b ) -> b -> List a -> b

A Reducer is a name for the type of the first argument of a fold. Consider a reducer of the form

Reducer state a b  = a -> (state, List b) -> (state, List b)

It fits into a fold of the form

Reducer state a b  -> (state, List b) -> List a -> (state, List b)

Let transform have type StateReducer state a b. Define

acc transformer state_ inputList =
  List.foldl transformer (state_, []) inputList

The type of this function is

Accumulator state a b = state -> List a -> (state, List b)

To restate in plainer English, an Accumulator state a b takes as input a State a b and a List a and returns a tuple consisting of an updated State a b and another list, one of type List b.

Accumulator.parse

Let us discuss the accumulators used in MiniLatex. The first of these is Accumulator.parse. Its function is to take a LatexState and a list of paragraphs, i.e., a List String, and produce an updated LatexState and a List (List LatexExpression). Each element of the latter is a List LatexExpression representing the application of the MiniLatex parser to a paragraph.

Accumulator.parse :
    LatexState
    -> List String
    -> ( LatexState, List (List LatexExpression) )
Accumulator.parse latexState paragraphs =
    paragraphs
        |> List.foldl parseReducer ( latexState, [] )

The Accumulator.parse function applies a List.foldl to a pair consisting of an initial LatexState and an empty list using a parseReducer. The latter is defined as follows:

The parseReducer takes as input a string, representing a logical paragraph of source text, and a pair consisting of a LatexState and a List (List LatexExpression. It parses the string and computes a new LatexState using latexStateReducer:

We discuss this function later on. The return value of the parseReducer is a pair consisting of the new LatexState and inputList with the parsedInput appended.

Accumulator.render

Accumulator.render renderer is an accumulator which takes a LatexState and a List (List LatexExpression) as input, and produces a new LatexState and List a as output. The nature of a depends on the renderer function used:

The expression renderReducer renderer is a reducer of type

It operates as follows:

  1. Compute a new LatexState by applying latexStateReducer to (a) the given list of LatexExpressions and (b) the LatexState coming from the first element of the second argument, a tuple of type (LatexState, List a)

  2. Apply the renderer to the new state computed in the previous step and the List LatexExpression given by the first argument.

  3. Return a tuple consisting of the updated LatexState and the inputList with the newly rendered text appended.

LatexStateReducer

The purpose of the latexStateReducer is to update a given LatexState using the information contained in a List LatexExpression, i.e., the parse result of a paragraph.

This a fairly complex reducer. In brief, it looks at the first LatexExpression in the List LatexExpression representing a given paragraph, then uses the function info to compute a record, theInfo, which extracts certain information from the paragraph. For example, if the paragraph is \section{Introduction}, the theInfo will contatin fields name = section and typ = macro. The call latexStateReducerDispatcher theInfo uses a dictionary lookup to produce a function of type LatexInfo -> LatexState -> LatexState from theInfo. Therefore the value of the expression

is of type LatexState, in accord with what one reads from the type annotation of latexStateReducer parsedParagraph latexState.

Let's see how this works in a simple example:

Now the dispatcher looks like this

where the dictionary has type

If the key given by theInfo is not in the latexStateReducerDict, projection of the arguments to LatexState is returned. In the case at hand, theInfo has fields name = "section" and typ = "macro", and the function returned by the dictionary is SRH.updateSectionNumber x y. Here SRH is an alias of the StateReducerHelper module. Referring to that module, we find that

Thus updateSectionNumber increments the "s1" counter in the latexState and sets the other section counters to zero. The addSection function, listed below, adds the current section to the table of contents field of the LatexState:

We will not go down the rabbit hole any further, but you get the general idea. The LatexState holds counters, the table of contents, cross references, and a general-purpose dictionaray. To add new abilities to MiniLatex, one can add new entries to latexStateReducerDict or new fields to LatexState.

Example: rendering a section

To conclude this discussion, we exhibit the render for sections: s

Appendix: the latexStateReducerDict

Some conclusions

The reliance of Accumulator.parse and Accumulator.render on the LatexState record, as well as the latexStateReducerDict for dispatching calls to latexStateReducer, makes it very easy to add new features, e.g., new macros and environments whose rendering requires a computed state. Moreover, it is not hard to add fields to LatexState in order to add further new features. Indeed, the first version of LatexInfo had only a counters field. Others were added later as the scope of MiniLatex grew.

Last updated