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 -> bA 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_, []) inputListThe 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:
Compute a new
LatexStateby applyinglatexStateReducerto (a) the given list ofLatexExpressionsand (b) theLatexStatecoming from the first element of the second argument, a tuple of type(LatexState, List a)Apply the
rendererto the new state computed in the previous step and theList LatexExpressiongiven by the first argument.Return a tuple consisting of the updated
LatexStateand theinputListwith 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