Blog › ICP guides
Refal developer on retainer: S.x single-symbol pattern, E.x expression variable, view field rewriting, Refal-5 sentences, and string-processing programming on monthly retainer
November 12, 2026 · ~15 min read
A Refal program performing recursive term transformation was producing four wrong match failures per run. The program used pattern matching on the view field to decompose structured terms: a sentence pattern used S.x OPEN-BRACKET <Fn S.x> CLOSE-BRACKET to match a symbol S.x followed by a parenthesized recursive call result. The pattern expected the recursive call <Fn S.x> to return exactly one symbol — which S.x can match — but the actual recursive call returned a term containing multiple symbols (a sequence of two or more atoms). In Refal, S.x (an S-variable) matches exactly one symbol in the active zone: one atom, one number, one character, one identifier. It cannot match a sequence of two or more terms. When the recursive call returned a multi-symbol result, the S.x pattern failed to match the position, causing the sentence not to fire and leaving the view field unrewritten. Four wrong match failures per run meant four inputs that should have been transformed were silently left unchanged. The Refal developer on retainer diagnosed the S.x/E.x arity confusion: S.x matches one symbol; E.x (an E-variable) matches zero or more terms — the correct choice for a position that may receive a variable-length sequence. Restructured the pattern to use E.result in the position after OPEN-BRACKET where the recursive call result lands: S.x OPEN-BRACKET E.result CLOSE-BRACKET (and renamed E.result to correctly capture the recursive result for use in the output expression). Wrong match failures per run: 4 → 0.
The work log entry read “fixed recursive term transformer pattern, 14h.” It names the symptom and duration. It cannot explain to a client why Refal’s S.x variable matches exactly one symbol and nothing else (Refal’s pattern variable taxonomy is strict: S-variables are single-symbol; T-variables match one term, which may be a symbol or a parenthesized group; E-variables match any sequence of zero or more terms; the variable kind is not inferred from context but is declared in the pattern itself through the S./T./E. prefix; using S.x where the matched expression may have variable length is a structural mismatch, not a runtime type error — the sentence simply will not fire for any input that puts more than one symbol in that position), why the fix required auditing every recursive call return value in every sentence pattern that used those results (a recursive function <Fn E.arg> that builds its result by prepending a computed prefix and appending a computed suffix produces a multi-symbol expression; any caller pattern that tries to capture that result in S.x will fail for any input that causes the recursion to prepend or append more than one symbol), or why the output expression restructuring to use the captured E.result correctly took separate remediation (the original output expression referenced S.x in the result position; after replacing S.x with E.result in the pattern, the output expression also needed to reference E.result rather than S.x; but the output expression also needed to handle the case where E.result is empty — zero symbols — which S.x structurally excluded; the empty-result case required a separate sentence). The 14 hours of S.x/E.x arity audit across all function definitions, recursive call return-value pattern redesign, and output expression restructuring are not visible in the diff beyond corrected variable prefix letters in two sentence patterns.
Refal view field and active expressions: S.x, E.x, T.x pattern variables and left-to-right matching
Refal’s computation model is built on the view field: a sequence of terms that is the entire state of a running Refal program. A Refal program rewrites the view field by matching function calls against function sentence patterns and replacing the matched call with the sentence’s result expression. Active expressions — function calls in angle brackets, written <F arg1 arg2 ...> — are the engine of computation. When the Refal interpreter encounters an active expression in the view field, it looks up the function name F and tries the function’s sentences in order; the first sentence whose pattern matches the arguments fires, and the entire <F args> expression is replaced with the sentence’s result expression. If no sentence matches, the program terminates with a recognition impossible error.
Pattern variable types are the most operationally important part of Refal for retainer engineers. S.x is an S-variable: it matches exactly one symbol — one atom, one number, one character, one identifier. It cannot match an empty sequence; it cannot match a sequence of two or more symbols; it cannot match a parenthesized group. E.x is an E-variable: it matches zero or more terms, making it the most flexible and most frequently used variable type in production Refal programs. An E-variable can match an empty sequence (no terms at all), a single symbol, or an arbitrarily long sequence of symbols and parenthesized subexpressions. T.x is a T-variable: it matches exactly one term, which may be either a single symbol or a complete parenthesized subexpression (a group enclosed in parentheses, which counts as one term regardless of what it contains). The T-variable is the correct choice when exactly one unit of structure is expected but that unit may be either a symbol or a grouped subexpression.
Left-to-right deterministic matching is how Refal resolves pattern matching. Pattern variables are bound greedily from left to right; E-variables in particular consume as few terms as possible initially and extend rightward only as needed to allow the rest of the pattern to match. This means the order of variables in a pattern matters operationally: an E-variable at the start of a pattern will try to match zero terms first, then one, then two, extending until the remaining pattern can match; an E-variable at the end of a pattern after all other variables have been bound will consume everything remaining. The S.x/E.x arity confusion is the most common pattern bug in Refal programs because the return value of a recursive call is often a sequence of unknown length: <Transform S.input> may return one symbol for a simple input and five symbols for a complex input, and the calling pattern’s variable type must be E.x (not S.x) to handle the general case. A retainer engineer’s first audit step on any Refal program with wrong match failures is to find every pattern position that receives a recursive call result and verify the variable there is E.x, not S.x or T.x.
Refal-5 sentences, function definitions, built-in functions, and module structure
Refal function definition syntax in Refal-5 is: function-name { pattern = result-expression; pattern = result-expression; ... }. Each alternative is called a sentence; sentences are separated by semicolons. The function name precedes the braces; the function has no explicit parameter list because all arguments arrive in the view field as a flat sequence matched by the pattern. Sentence alternatives are tried in the order they are written: the first sentence whose pattern matches the incoming argument sequence fires. This order-sensitivity is critical for correct Refal program design: a more general sentence (with a broad E.x pattern that can match almost any input) placed before a more specific sentence (with a narrow pattern for a particular structure) will shadow the specific sentence entirely, because the general sentence fires first for every input that could also match the specific one. The retainer work of sentence ordering analysis involves identifying every pair of sentences in a function where one sentence’s pattern is a superset of another’s, and verifying that the more specific (narrower) sentence appears first. The rewriting continues until the view field contains no more active expressions: no more function calls in angle brackets, only passive terms.
Refal built-in functions form the standard vocabulary for Refal programs. Symb converts a number or character to its symbolic representation. Numb converts a symbol to its numeric value if it represents a number. Char converts an integer to the corresponding character. Type returns a two-character code identifying the type of its argument: 'N ' for number, 'L ' for lower-case letter, 'U ' for upper-case letter, 'P ' for punctuation, and so on — making Type the standard dispatch mechanism for type-conditional Refal programs. Compare takes two values and returns ‘+’ (greater), ‘0’ (equal), or ‘−’ (less), enabling ordered comparison for sorting and branching. Arithmetic: Add, Sub, Mul, Div, Mod (also written as the symbolic operators +, −, *, //, and %% in some Refal variants) perform integer arithmetic on Refal number values. First returns the first term of a sequence; Last returns the last term; Tail returns the sequence with its first term removed, enabling list-processing traversal. Prout prints its argument expression to standard output. Card reads one line from standard input and returns it as a character sequence. Apply applies a function value (computed at runtime as an identifier) to an argument expression: <Apply E.fn E.args> is the mechanism for higher-order programming in Refal.
File I/O in Refal-5 uses Open (open a named file for reading or writing, associating it with a channel number), Get (read one line from an open channel), Put (write a line to an open channel), and Erase (close and erase a file). The channel number is a small integer that serves as the file handle. Cross-module function references are declared with $EXTERN FunctionName; at the top of a module: this tells the Refal-5 compiler that FunctionName is defined in another module and should be resolved at link time rather than within the current source file. The entry point of a Refal-5 program is marked with $ENTRY Go { ... } (or another name used in the $ENTRY declaration); the $ENTRY marker designates the function that the runtime calls to start execution. Refal variant landscape: Refal-2 is the original Turchin design from the 1960s–1970s, with a simpler syntax and no explicit module system; Refal-5 is the most widely implemented variant and the standard for modern Refal work, developed at the Keldysh Institute of Applied Mathematics in Moscow; Refal-6 adds a type system and explicit module declarations; Refal-Plus extends Refal-5 with a more structured module system and additional control flow. The termination guarantee is absent: Refal programs can diverge through mutual recursion; the $DRIVE form in Refal-6 provides limited divergence control.
Refal supercompilation and metaprogramming: Apply, function values, and the Turchin legacy
Refal’s historical significance in programming language theory is substantial. Valentin Turchin designed Refal in 1966 at the Keldysh Institute of Applied Mathematics as a language for symbolic artificial intelligence and metaprogramming. The name is an acronym: Recursive Functions Algorithmic Language. Refal was used throughout the 1970s and 1980s for theorem provers, language transformers, and compiler generators, but its deepest contribution to computer science is the theoretical framework Turchin developed using Refal programs as the object of study: supercompilation, a theory of program transformation through positive information propagation. A Refal program run by the Refal supercompiler produces a more efficient specialized version of itself: the supercompiler partially evaluates the program with respect to its static inputs, propagating information about pattern matches forward through the computation and folding redundant checks, divergent branches, and repeated computations. Supercompilation differs from classical partial evaluation in that it also folds loops and eliminates pattern checks that the propagated information renders unreachable. The Refal community produced two major supercompiler implementations: SIFTS (Supercompiler for Intermediate Functional Term Systems, developed in the 1980s) and SCP4 (Supercompiler Refal-5, developed by Andrei Klimov and colleagues at the Keldysh Institute). Both take Refal-5 programs as input and produce Refal-5 programs as output.
Apply and function-value programming is Refal’s mechanism for higher-order computation. Apply takes a function name (as an identifier value, stored in an E-variable) and an argument expression, and applies the named function to those arguments: <Apply E.fn E.args>. Because Refal function names are first-class identifier values, a program can compute a function name at runtime — select from a table of function names, construct a name by concatenation, or receive a name as a parameter — and then dispatch to it with Apply. This enables parameterized transformation architectures where the transformation strategy is passed as a function-value argument rather than hardcoded in pattern matching. The common pattern for function-value dispatch: a driver function receives a strategy name in an E-variable, passes it together with the data to a dispatcher, and the dispatcher calls <Apply E.strategy E.data>. Without Apply, all dispatching in Refal must be done through explicit pattern matching on strategy names in sentences, which requires listing every possible strategy name in the function definition and prevents runtime composition of strategies.
Retainer work on Refal programs used as transformation engines covers the full spectrum of SCP4 and SIFTS preparation. Running a Refal program through SCP4 requires that the program be structured to expose the static inputs that the supercompiler can propagate: functions that mix static and dynamic computation must be split so that the static-input-dependent portion is exposed as a separate function that SCP4 can specialize; recursive functions must terminate for the inputs given to the supercompiler (SCP4 uses a homeomorphic embedding check to detect potential divergence and fold the computation, but programs that diverge in complex ways may cause SCP4 to produce very large specialized programs rather than compact ones). Retainer work for supercompilation-targeted Refal includes restructuring programs to separate static and dynamic inputs, naming the functions that SCP4 should specialize, and validating the output of SCP4 against the original program semantically. The Apply-based dynamic dispatch patterns that are useful for flexibility in interpreted Refal programs can be counterproductive for SCP4 input: if the function name in an Apply call is not statically known, SCP4 cannot specialize through the Apply call, leaving a dynamic dispatch in the specialized output where static dispatch was intended. Retainer engineers working on SCP4 pipelines must balance the flexibility of Apply-based dispatch against the specialization requirements of the supercompiler.
How HourTab tracks Refal developer retainer hours
Refal retainer work shares the invisible-work problem common to all language engineering retainers, compounded by the fact that Refal’s most common retainer tasks — S.x/E.x arity audit, sentence ordering analysis, Apply dispatch redesign, SCP4 input preparation — produce diffs whose surface area is small relative to the diagnostic work. An S.x-to-E.x fix is a diff with one letter changed in one pattern variable prefix; the value is elimination of all wrong match failures from the affected recursive call path, correct pattern variable arity discipline enforced across every sentence in the function, and a correct understanding of Refal’s variable type taxonomy that prevents the same arity mismatch from appearing in the next recursive function the team writes. A sentence ordering fix that promotes a specific-pattern sentence above a general E.x sentence is a diff with two sentences swapped; the value is elimination of all wrong rewrites caused by the general sentence shadowing the specific one, correct termination behavior for all inputs that should have matched the specific sentence, and a clear understanding of Refal’s first-match semantics that the team can apply to every new function definition. An Apply dispatch redesign that replaces hardcoded strategy names in pattern matching with a computed function-value architecture is a diff that restructures multiple function definitions; the value is a parameterized transformation engine that can accept new strategies without modifying existing function definitions, a reduction in sentence count across all dispatching functions, and a correct Apply-based dispatch pattern that can be reused across the codebase.
HourTab gives Refal developers a public retainer-hours URL they send to clients — typically research groups at institutes of applied mathematics maintaining Refal transformation engines, compiler groups using Refal for meta-level program analysis, and academic groups working with SCP4 or SIFTS for program specialization — at the start of an engagement. For Refal retainers, each work log entry should name the mechanism (S.x/E.x/T.x pattern variable arity audit; recursive call return-value pattern design; sentence ordering analysis — specific before general; OPEN-BRACKET/CLOSE-BRACKET parenthesized subexpression structure; left-to-right match discipline; Symb/Numb/Char/Type type dispatch; Compare and arithmetic built-ins; First/Last/Tail list operations; Prout/Card I/O; Apply function-value dispatch; $EXTERN cross-module declaration; $ENTRY point design; SCP4 supercompiler input preparation), the specific function and variable names involved in the bug, the S.x/E.x arity mismatch or sentence ordering failure, and the before/after metric. Refal retainers are often compared to Prolog developer retainers for logic and symbolic programming comparison, and to Erlang developer retainers for functional pattern-matching engineering. HourTab’s work log makes the S.x/E.x arity audit, sentence reordering analysis, and Apply dispatch redesign visible to clients who would otherwise see only the symptom — wrong match failures or wrong rewrites — and not understand why the fix required understanding Refal’s strict variable-type taxonomy and first-match sentence semantics.
Track Refal developer retainer hours without the status emails
HourTab gives Refal developers a public URL per client retainer. One link, no login, live burn-down. Your clients stop asking “how many hours do I have left?” and your work log becomes the proof of value that gets the retainer renewed.
See HourTab pricing →FAQ: Refal developer retainers
What does a Refal developer on retainer typically do?
A Refal developer on monthly retainer covers four principal service areas: pattern variable design (S.x single-symbol vs E.x expression vs T.x term arity audit; recursive call result pattern design — always E.x for positions that receive multi-term recursive results; left-to-right pattern match discipline; sentence ordering and backtracking analysis; parenthesized subexpression structure with OPEN-BRACKET/CLOSE-BRACKET); built-in function sequence design (Symb/Numb/Char/Type type dispatch; Compare and arithmetic; First/Last/Tail list operations; Prout/Card I/O; Apply function-value dispatch; Open/Get/Put file I/O); function definition and module structure ($EXTERN declarations; $ENTRY point design; Refal-5 semicolon-delimited sentence alternatives; Refal-2/Refal-5/Refal-6 variant compatibility); and transformation architecture design (term-rewriting pipeline design; recursive transformation function authorship; supercompiler input program preparation for SCP4; metaprogramming with Apply and computed function names).
What Refal work is most commonly underlogged in a retainer?
S.x/E.x arity mismatch repair (pattern used S.x in position receiving multi-symbol recursive result; S.x failed to match multi-symbol term; 4 wrong match failures/run; restructured to E.result; wrong failures: 4/run → 0; 10–18 hrs invisible in all recursive call return-value arity audit, S.x/E.x variable type decision for each pattern position, and output expression restructuring to use captured E.result correctly), sentence ordering deadlock (two sentences with overlapping patterns where the more specific sentence was listed after the more general E.x sentence; general sentence always matched first; specific sentence never fired; 6 wrong rewrites/run; reordered sentences specific-first; wrong rewrites: 6/run → 0; 8–14 hrs invisible in sentence ordering analysis across all function definitions), and Apply dispatch design for computed function names (function dispatch used hardcoded names in pattern matching instead of Apply for parameterized transformation; restructured to use E.fn variable and Apply dispatch; 12–20 hrs invisible in transformation architecture redesign).
What are typical Refal developer retainer rates?
Entry-level Refal developers (1–2 years, basic S.x/E.x/T.x patterns, simple function definitions, Prout/Card I/O, Refal-5 sentence syntax) bill at $65–$110/hr. Mid-level Refal engineers (2–4 years, recursive transformation function design, Apply higher-order dispatch, First/Last/Tail list processing, cross-module $EXTERN design, Refal-2/Refal-5 compatibility) bill at $105–$185/hr. Senior Refal architects (4–8 years, supercompiler input program preparation for SCP4/SIFTS, complex term-rewriting architecture, Turchin supercompilation theory, Refal-6 type system design, large-scale transformation engine maintenance) bill at $155–$275/hr. Monthly retainer ranges: $1,600–$4,200/mo advisory (15–25 hrs), $6,000–$15,000/mo for full transformation engine engagements.
What should a Refal developer retainer agreement include?
A Refal developer retainer agreement should specify: pattern design scope (S.x/E.x/T.x variable arity audit; recursive call return-value pattern design; sentence ordering analysis; parenthesized subexpression structure; left-to-right match discipline); built-in function scope (Symb/Numb/Char/Type type dispatch; Compare arithmetic; First/Last/Tail list operations; Prout/Card I/O; Apply function dispatch; Open/Get/Put file I/O); module scope if applicable ($EXTERN declarations; $ENTRY point design; Refal-2/Refal-5/Refal-6 variant selection); transformation architecture scope (term-rewriting pipeline; recursive function design; supercompiler input preparation); and hour logging format (operation type: pattern arity audit, sentence ordering analysis, Apply dispatch design; before/after error metric; Refal variant: Refal-2/Refal-5/Refal-6/Refal-Plus; execution environment: REFAL5 interpreter, SCP4 supercompiler; whether fix was S.x→E.x variable type change, sentence reordering, or Apply dispatch restructuring).
How should Refal developer retainer hours be logged?
Log each Refal retainer session with: advisory category (S.x/E.x/T.x pattern variable arity audit; recursive call return-value pattern design; sentence ordering analysis — specific before general; parenthesized subexpression OPEN-BRACKET/CLOSE-BRACKET structure; left-to-right match discipline; Symb/Numb/Char/Type type dispatch; Compare and arithmetic built-ins; First/Last/Tail list operations; Prout/Card I/O; Apply function-value dispatch; $EXTERN cross-module declarations; $ENTRY point design; supercompiler input preparation for SCP4), the specific function and variable names involved in the bug (transformation function pattern used S.x in position receiving recursive call result <Fn S.x> that returned multi-symbol term; S.x failed to match; 4 wrong match failures/run; restructured to E.result; wrong failures: 4/run → 0), and the before/after observable metric. Include Refal variant (Refal-2/Refal-5/Refal-6) and execution environment (REFAL5 interpreter, SCP4, SIFTS), and whether the fix was S.x→E.x type change, sentence reordering, Apply dispatch redesign, or $EXTERN path fix.