Contextual overloading |
For large software projects, it is important that different modules can be developed as independently as possible one from each other. Furthermore, fundamental modules often implement default behaviour which is to be overwritten in a more specialized module. In order to facilitate these two requirements, TeXmacs implements an advanced system of contextual overloading.
In order to get the main idea behind this system, consider the implementation of a given functionality, like hitting the return key. Depending on the context, different actions have to be undertaken: by default, we start a new paragraph; inside a table, we start a new row; etc. A naive implementation would check all possible cases in a routine kbd-return and call the corresponding routine. However, this makes it impossible to add a new case in a new module without modifying the module which defines kbd-return. By contrast, the system of contextual overloading allows the user to conditionally redefine the routine kbd-return several times in distinct modules.
For instance, assume that we want to define a function hello which inserts “Hello” by default, but “hello()” in mode math, while positioning the cursor between the brackets. Using contextual overloading, this may be done as follows:
(tm-define (hello) (insert "Hello"))
(tm-define (hello) (:mode in-math?) (insert-go-to
"hello()" '(6)))
Here we recall that the two definitions may be put inside different modules. Notice also that the contextual overloading system considers the implementation of hello inside math mode to be more particular than the default implementation. In case when several implementations match their respective conditions, the most particular implementation will be chosen.
Currently, TeXmacs supports three major types of conditions:
A TeXmacs mode corresponds to a simple predicate without arguments, together with a finite number of other, less particular, modes which are implied by it. New modes can be defined using the instruction
(texmacs-modes mode% cond
submode-1 ... submode-n)
For instance, we might define a new mode inside-theorem? as follows
(texmacs-modes inside-theorem% (inside? 'theorem)
in-text%)
Some standard modes are always?, in-source?, in-text?, in-math?, in-prog?. There is also a special mode prevail? which is always satisfied, but nevertheless considered as more particular than all other modes.
Certain actions only make sense when the cursor is inside a
special tag, or more generally inside some structure which matches
a predicate. For instance, the action structured-insert-right
inserts a new column at the right-hand side of the cursor when you
are inside a
For instance, consider the implementation of a routine structured-swap for fractions, which permutes the numerator and denominator. This may be done as follows:
(tm-define (structured-swap)
(:inside frac)
(with-innermost t 'frac
(tree-set! t ‘(frac ,(tree-ref t 1)
,(tree-ref t 0)))))
This type of contextual overloading is closest to the more
classical concept of overloading used by languages like
(tm-define (my-replace what by)
default-implementation)
(tm-define (my-replace what by)
(:require (== what by))
(noop))
In cases of conflict, we notice that the contextual overloading system first dispatches on mode, next on the cursor context and finally on the arguments on the function. For instance, consider a routine foo defined by
(tm-define (foo) (:mode in-math?)
implementation-1)
(tm-define (foo) (:inside 'frac)
implementation-2)
Then the first implementation will be used when foo is called from within a fraction in math mode.
Besides tm-define, several other added language primitives support the contextual overloading mechanism. For instance, kbd-map, define-menu and extend-menu support overloading on mode and cursor context. The tm-define-macro and tm-property primitives are analoguous to tm-define.