_Reduction Semantics_ _reduction-semantics_ This collection provides three files: _reduction-semantics.ss_: the core reduction semantics library _gui.ss_: a _visualization tool for reduction sequences_. _subst.ss_: a library for _capture avoiding substitution_. In addition, the examples subcollection contains several small languages to demonstrate various different uses of this tool: _arithmetic.ss_: an arithmetic language with every possible order of evaluation _church.ss_: church numerals with call by name normal order evaluation _combinators.ss_: fills in the gaps in a proof in Berendrecht that i and j (defined in the file) are a combinator basis _future.ss_: an adaptation of Cormac Flanagan's future semantics. _ho-contracts.ss_: computes the mechanical portions of a proof in the Contracts for Higher Order Functions paper (ICFP 2002). Contains a sophisticated example use of an alternative pretty printer. _macro.ss_: models macro expansion as a reduction semantics. _omega.ss_: the call by value lambda calculus with call/cc. Includes omega and two call/cc-based infinite loops, one of which has an ever-expanding term size and one of which has a bounded term size. _threads.ss_: shows how non-deterministic choice can be modeled in a reduction semantics. Contains an example use of a simple alternative pretty printer. _semaphores.ss_: a simple threaded language with semaphores iswim.ss : see further below. ====================================================================== The _reduction-semantics.ss_ library defines a pattern language, used in various ways: pattern = any | number | string | variable | (variable-except ...) | hole | (hole ) | | (name ) | (in-hole ) | (in-named-hole ) | (side-condition ) | (cross >) | (pattern ...) | The patterns match sexpressions. The _any_ pattern matches any sepxression. The _number_ pattern matches any number. The _string_ pattern matches any string. Those three patterns may also be suffixed with an underscore and another identifier, in which case they bind the full name (as if it were an implicit `name' pattern) and match the portion before the underscore. The _variable_ pattern matches any symbol. The _variable-except_ pattern matches any variable except those listed in its argument. This is useful for ensuring that keywords in the language are not accidentally captured by variables. The _hole_ pattern matches anything when inside a matching in-hole pattern. The (hole ) variation on that pattern is used in conjunction with in-named-hole to support languages that require multiple patterns in a hole. The __ pattern stands for a literal symbol that must match exactly, unless it is the name of a non-terminal in a relevant language or contains an underscore. If it is a non-terminal, it matches any of the right-hand sides of that non-terminal. If the symbol is a non-terminal followed by an underscore, for example e_1, it is implicitly the same as a name pattern that matches only the non-terminal, (name e_1 e) for the example. If the symbol otherwise has an underscore, it is an error. _name_: The pattern: (name ) matches and binds using it to the name . _in-hole_: The (in-hole ) matches the first pattern. This match must include exactly one match against the `hole' pattern. The `hole' pattern matches any sexpression. Then, the sexpression that matched the hole pattern is used to match against the second pattern. _in-named-hole_: The pattern: (in-named-hole ) is similar in spirit to in-hole, except that it supports languages with multiple holes in a context. The first argument identifies which hole, using the (hole ) pattern that this expression requires and the rest of the arguments are just like in-hole. That is, if there are multiple holes in a term, each matching a different (hole ) pattern, this one selects only the holes that are named by the first argument to in-named-hole. _side-condition_: The (side-condition pattern guard) pattern matches what the embedded pattern matches, and then the guard expression is evaluated. If it returns #f, the pattern fails to match, and if it returns anything else, the pattern matches. In addition, any occurrences of `name' in the pattern are bound using `term-let' (see below) in the guard. _cross_: The (cross ) pattern is used for the compatible closure functions. If the language contains a non-terminal with the same name as , the pattern (cross ) matches the context that corresponds to the compatible closure of that non-terminal. The (pattern ...) pattern matches a sexpression list, where each pattern matches an element of the list. In addition, if a list pattern contains an ellipses that is not treated as a literal, instead it matches any number of duplications of the pattern that came before the ellipses (including 0). Furthermore, each (name ) in the duplicated pattern binds a list of matches to , instead of a single match. (A nested duplicated pattern creates a list of list matches, etc.) Ellipses may be placed anywhere inside the row of patterns, except in the first position or immediately after another ellipses. Multiple ellipses are allowed. > (language (non-terminal pattern ...) ...) SYNTAX This form defines a context-free language. As an example, this is the lambda calculus: (define lc-lang (language (e (e e ...) variable v) (c (v ... c e ...) hole) (v (lambda (variable ...) e)))) with non-terminals e for the expression language, c for the evaluation contexts and v for values. > compiled-lang? : (any? . -> . boolean?) Returns #t if its argument was produced by `language', #f otherwise. > (term-let ([tl-pat expr] ...) body) SYNTAX Matches each given id pattern to the value yielded by evaluating the corresponding expr and binds each variable in the id pattern to the appropriate value (described below). These bindings are then accessible to the `term' syntactic form. Identifier-patterns are terms in the following grammar: tl-pat ::= identifier | (tl-pat-ele ...) tl-pat-ele ::= tl-pat | tl-pat ellipses where ellipses is the literal symbol consisting of three dots (and the ... indicates repetition as usual). If tl-pat is an identifier, it matches any value and binds it to the identifier, for use inside `term'. If it is a list, it matches only if the value being matched is a list value and only if every subpattern recursively matches the corresponding list element. There may be a single ellipsis in any list pattern; if one is present, the pattern before the ellipses may match multiple adjacent elements in the list value (possibly none). > (term s-expr) SYNTAX This form is used for construction of new s-expressions in the right-hand sides of reductions. It behaves identically to quasiquote except that names bound by `term-let' are implicitly substituted with the values that those names were bound to, expanding ellipses as in-place sublists (in the same manner as syntax-case patterns). For example, (term-let ([body '(+ x 1)] [(expr ...) '(+ - (values * /))] [((id ...) ...) '((a) (b) (c d))]) (term (let-values ([(id ...) expr] ...) body))) evaluates to '(let-values ([(a) +] [(b) -] [(c d) (values * /)]) (+ x 1)) It is an error for a term variable to appear in an expression with an ellipsis-depth different from the depth with which it was bound by `term-let'. It is also an error for two `term-let'-bound identifiers bound to lists of different lengths to appear together inside an ellipsis. > (reduction language pattern bodies ...) SYNTAX This form defines a reduction. The first position must evaluate to a language defined by the `language' form. The pattern determines which terms this reductions apply to and the last bodies are expressions that must evaluate to a new sexpression corresponding to the reduct for this term. The bodies are implicitly wrapped in a begin. As an example, the reduction for the lambda calculus above would be written like this: (reduction lc-lang (in-hole (name c c) ((lambda (variable_i ...) e_body) v_i ...)) (plug (term c) (foldl lc-subst (term e_body) (term (v_i ...)) (term (variable_i ...))))) The in-hole pattern matches a term against `c', and binds what matches the hole to `hole'. Then, it matches what matched the hole against ((lambda (variable) e) v). Because of the name pattern and the subscripted variable names we used, c, hole, variable_i, e_body, and v_i are bound with `term-let' in the right hand side of the reduction. Then, when the reduction matches, the result is the result of evaluating the last argument to reduction. See `plug' below and lc-subst is defined by using the subst.ss library below. > (reduction/context language context pattern bodies ...) SYNTAX reduction/context is a short-hand form of reduction. It automatically adds the `in-hole' pattern and the call to `plug'. The equivalent reduction/context to the above example is this: (reduction/context lang c ((lambda (variable_i ...) e_body) v_i ...) (lc-subst (term (variable_i ...)) (term (v_i ...)) (term e_body)))) > red? : (any? . -> . boolean?) Returns #t if its argument is a reduction (produced by `reduction', `reduction/context', etc.), #f otherwise. > compatible-closure : (red? compiled-lang? symbol? . -> . red?) This function accepts a reduction, a language, the name of a non-terminal in the language and returns the compatible closure of the reduction for the specified non-terminal. > context-closure : (red? compiled-lang? any . -> . red?) This function accepts a reduction, a language, a pattern representing a context (ie, that can be used as the first argument to `in-hole'; often just a non-terminal) in the language and returns the closure of the reduction in that context. > plug : (any? any? . -> . any) The first argument to this function is an sexpression to plug into. The second argument is the sexpression to replace in the first argument. It returns the replaced term. > reduce : ((listof red?) any? . -> . (listof any?)) Reduce accepts a list of reductions, a term, and returns a list of terms that the term reduces to. > language->predicate : (compiled-lang? symbol? . -> . (any? . -> . boolean?)) Takes a language and a non-terminal name and produces a predicate that matches instances of the non-terminal. > match-pattern : (compiled-pattern any/c . -> . (union false? (listof mtch?))) This is mostly used to explore a particular pattern to determine what is going wrong. It accepts a compiled pattern and a and returns #f if the pattern doesn't match the term, and returns a list of mtch structure if the terms do match. > compile-pattern : (compiled-lang? any? . -> . compiled-pattern) This is mostly used in conjunction with match-pattern to explore particular patterns and terms when debugging. It compiles a sexpression pattern to a pattern. > mtch? : (any/c . -> . boolean?) Determines if a value is a mtch structure. > mtch-bindings : (mtch? -> bindings?) This returns a bindings structure (see below) that binds the pattern variables in this match. > mtch-context : (mtch? . -> . any/c) Returns the current context being built up for a match. Usually, this is the same as the original term being matched. > mtch-hole : (mtch? . -> . (union none? any/c)) Returns the current contents of the hole for this match (if there was a decomposition). Usually returns none. > variable-not-in : (any? symbol? . -> . symbol?) This helper function accepts an sexpression and a variable. It returns a variable not in the sexpression with a prefix the same as the second argument. > make-bindings : ((listof rib?) . -> . bindings?) > bindings? : (any? . -> . boolean?) > bindings-table : (bindings? . -> . (listof rib?)) Constructor, predicate, and selector functions for the bindings values returned by match-pattern. Each bindings value represents the bindings established for a single parse of the term; multiple such parses may be possible in some situations. > make-rib : (symbol? any? . -> . rib?) > rib? : (any? . -> . boolean?) > rib-name : (rib? . -> . symbol?) > rib-exp : (rib? . -> . any?) Constructor, predicate, and selector functions for the rib values contained within a bindings. Each rib associates a name with an s-expression from the language, or a list of such s-expressions, if the (name ...) clause is followed by an ellipsis. Nested ellipses produce nested lists. ====================================================================== The _gui.ss_ library provides three functions: > (traces language reductions expr [pp]) This function calls traces/multiple with language, reductions and (list expr). > (traces/multiple lang reductions exprs [pp]) This function calls traces/pred with the predicate (lambda (x) #t) > (traces/pred lang reductions exprs pred [pp]) lang : language reductions : (listof reduction) exprs : (listof sexp) pred : (sexp -> boolean) pp : (any -> string) | (any port number (is-a?/c text%) -> void) This function opens a new window and inserts each expr. Then, reduces the terms until either reduction-steps-cutoff (see below) different terms are found, or no more reductions can occur. It inserts each new term into the gui. Clicking the `reduce' button reduces until reduction-steps-cutoff more terms are found. The pred function indicates if this term has a particular property. If the function returns #t, the term is displayed normally. If it returns #f, the term is displayed with a red border. If the pp function can take four arguments, it renders its first argument into the port (its second argument) with width at most given by the number (its third argument). The final argument is the text where the port is connected -- characters written to the port go to the end of the editor. If the pp function cannot take four arguments, it is instead invoked with a single argument, the s-expression to render, and it must return a string which the GUI will use as a representation of the given expression for display. The default pp uses MzLib's pretty-print function. See threads.ss in the examples directory for an example use of the one-argument form of this argument and ho-contracts.ss in the examples directory for an example use of its four-argument form. You can save the contents of the window as a postscript file from the menus. > (reduction-steps-cutoff) > (reduction-steps-cutoff number) A parameter that controls how many steps the `traces' function takes before stopping. > (initial-font-size) > (initial-font-size number) A parameter that controls the initial font size for the terms shown in the GUI window. > (initial-char-width) > (initial-char-width number) A parameter that determines the initial width of the boxes where terms are displayed (measured in characters) > (dark-pen-color color-or-string) > (dark-pen-color) => color-or-string > (dark-brush-color color-or-string) > (dark-brush-color) => color-or-string > (light-pen-color color-or-string) > (light-pen-color) => color-or-string > (light-brush-color color-or-string) > (light-brush-color) => color-or-string These four parameters control the color of the edges in the graph. ====================================================================== The _subst.ss_ library provides these names: > (subst (match-pattern subst-rhs ...) ...) SYNTAX The result of this form is a function that performs capture avoiding substitution for a particular (sexp-based) language. The function accepts three arguments, a variable, a term to substitute and a term to substitute into. Each of the `match-pattern's specify the forms of the language and the `subst-rhs's specify what kind of form it is. Each of the match-patterns are in (lib "match.ss" "match")'s pattern language and any variable that they bind are available in the 's described below. The language of the subst-rhs follows. > (variable) this means that the rhs for this form is a symbol that should be treated like a variable. Nothing may follow this. > (constant) this means that the rhs for this form is a constant that cannot be renamed. Nothing may follow this. > (all-vars ) This form indicates that this pattern in the language binds the variables produced by the . Immediately following this in a subst-rhs must be a (build ...) form and some number of (subterm ...) or (subterms ...) forms. > (build ) This form must come right after an (all-vars ...) form and before any (subterm ...) or (subterms ...) forms. This form tells subst how to reconstruct this term. The must evaluate to a procedure that accepts the (possibly renamed) variables from the all-vars clause, and one argument for each of the subterms that follow this declaration (with subterms flattened into the argument list) in the same order that the subterm or subterms declarations are listed. > (subterm ) The first must be a list of variables that is a sub-list of the variables in the all-vars expression. The second expression must be an sexp corresponding to one of the subexpressions of this expression (matched by the match-patten for this clause of subst). > (subterms ) The first must be a list of variables that is a sub-list of the variables in the all-vars expression. The second expression must be an sexp corresponding to one of the subexpressions of this expression (matched by the match-patten for this clause of subst). Consider this example of a substitution procedure for the lambda calculus: (define lc-subst (subst [`(lambda ,vars ,body) (all-vars vars) (build (lambda (vars body) `(lambda ,vars ,body))) (subterm vars body)] [(? symbol?) (variable)] [(? number?) (constant)] [`(,fun ,@(args ...)) (all-vars '()) (build (lambda (vars fun . args) `(,fun ,@args))) (subterm '() fun) (subterms '() args)])) The first clause matches lambda expressions with any number of arguments and says that there is one subterm, the body of the lambda, and that all of the variables are bound in it. The second clause matches symbols and indicates that they are variables. The third clause matches numbers and indicates that they are constants. The final clause matches function applications. The `all-vars' shows that applications introduce no new names. The build procedure reconstructs a new application form. The subterm declaration says that the function position is a subterm with no variables bound in it. The subterms declaration says that all of the arguments are subterms and that they do not introduce any new terms. In this program, lc-subst is bound to a function that does the substitution. The first argument is the variable to substitute for, the second is the term to substitute and the final argument is the term to substitute into. For example, this call: (lc-subst 'q '(lambda (x) y) '((lambda (y) (y q)) (lambda (y) y))) produces this output: '((lambda (y@) (y@ (lambda (x) y))) (lambda (y) y)) This library also provides: > (plt-subst (match-pattern subst-rhs ...) ...) SYNTAX This is identical to subst, described above, except that the pattern language used is that from (lib "plt-match.ss"), instead of (lib "match.ss"). > subst/proc > alpha-rename > free-vars/memoize Theses functions are the procedure-based interface to substitution that subst expands to and uses. ====================================================================== The _iswim.ss_ module in the "examples" sub-collection defines a grammar and reductions from "Programming Languages and Lambda Calculi" by Felleisen and Flatt. Example S-expression forms of ISWIM expressions: Book S-expr ---- ------ (lambda x . x) ("lam" x x) (+ '1` '2`) ("+" 1 2) ((lambda y y) '7`) (("lam" y y) 7) CK machine: Book S-expr ---- ------ <(lambda x . x), mt> (("lam" x x) : "mt") CEK machine: Book S-expr ---- ------ <<(lambda x . x), ((("lam" x x) {>}>, : ((X (5 : ())))) mt> : "mt") The full grammar: (language (M (M M) (o1 M) (o2 M M) V) (V X ("lam" variable M) b) (X variable) (b number) (o1 "add1" "sub1" "iszero") (o2 "+" "-" "*" "^") (on o1 o2) ;; Evaluation contexts: (E hole (E M) (V E) (o1 E) (o2 E M) (o2 V E)) ;; Continuations (CK machine): (k "mt" ("fun" V k) ("arg" M k) ("narg" (V ... on) (M ...) k)) ;; Environments and closures (CEK): (env ((X = vcl) ...)) (cl (M : env)) (vcl (V- : env)) ;; Values that are not variables: (V- ("lam" variable M) b) ;; Continuations with closures (CEK); (k- "mt" ("fun" vcl k-) ("arg" cl k-) ("narg" (vcl ... on) (cl ...) k-))) The following are provided by "iswim.ss" (case-insensitively): Grammar and substitution: > iswim-grammar : compiled-lang? > M? : (any? . -> . boolean?) > V? : (any? . -> . boolean?) > o1? : (any? . -> . boolean?) > o2? : (any? . -> . boolean?) > on? : (any? . -> . boolean?) > k? : (any? . -> . boolean?) > env? : (any? . -> . boolean?) > cl? : (any? . -> . boolean?) > vcl? : (any? . -> . boolean?) > k-? : (any? . -> . boolean?) > iswim-subst : (M? symbol? M? . -> . M?) > empty-env : env? > env-extend : (env? symbol? vcl? . -> . env?) > env-lookup : (env? symbol? . -> . (union false? vcl?)) Reductions: > beta_v : red? > delta : (listof red?) > ->v : (listof red?) > :->v : (listof red?) Abbreviations: > if0 : (M? M? M? . -> . M?) > true : M? > false : M? > mkpair : M? > fst : M? > snd : M? > Y_v : M? > sum : M? Helpers: > delta*1 : (o1? V? . -> . (union false? V?)) delta as a function for unary operations. > delta*2 : (o2? V? V? . -> . (union false? V?)) delta as a function for binary operations. > delta*n : (on? (listof V?) . -> . (union false? V?)) delta as a function for any operation. ====================================================================== The _helper.ss_ module provides additional helper functions and syntax. ------- Memoization ------- > (define-memoized (f arg ...) expr ...) SYNTAX Like `define' for function definitions, except that the function is "memoized": it automatically remembers each result, and when `eq?' arguments are provided later, the same result is returned immediately. > (lambda-memoized (arg ...) expr) SYNTAX Like `lambda' except that the resulting function is "memoized" (see `define-memoized' above). ------- Matching ------- > (lang-match-lambda (variable) grammar-expr [pattern bodies ...] ...) SYNTAX Combines a pattern-matching dispatch with a `lambda', producing a function that takes a single argument and pattern-matches. Each `pattern' is as for `reduction', and the names that the pattern binds are visible in the `bodies'. > (lang-match-lambda* (variable ...) main-variable grammar-expr [pattern bodies ...] ...) SYNTAX Generalizes `lang-match-lambda' to create a multi-arity procedure. The `main-variable' must be one of the `variables', and it is the variable used for matching. > lang-match-lambda-memoized SYNTAX > lang-match-lambda-memoized* SYNTAX Memoized versions of the above. ------- Backtracking ------- A function that supports backtracking must returns one of the following: * #f to indicate failure * any other value to indicate simple success * (many-results l) to indicates success with any of the results in the list l * (explore-results ...) or (explore-parallel-results ...), which explores the results of recursive calls to generate one or more result values lazily Whenever the backtracking function calls itself (or another backtracking function in a cooperative set), it should use `explore-results' or `explore-parallel-results' instead of inspecting the value directly. To get a single result from the backtracking function, uses `first-result'. > many-results : ((listof any) . -> . any) Aggregates potential results for future exploration. > (explore-results (id) backtrackable-expr SYNTAX body-expr ...) Evaluates `backtrackable-expr'. In the simple case, `id' is bound to the result, the the result of the `explore-results' expression is the result of evaluating `body-expr's. If `backtrackable-expr' produces multiple alternatives (via `many-results') or if it produces a backtrackable computation, the result is a backtrackable computation. This backtrackable computation proceeds bey generating each successful result from `backtrackable-expr', and then feeding the result to the `body-expr's to refine the results. Naturally, `body-expr' can contain further uses of `explore-results' or `many-results' which are folded into the backtracking exploration. > (explore-parallel-results (results-id) backtrackable-list-expr body-expr ...) Generalizes `explore-results' where `backtrackable-list-expr' produces a list of backtrackable results, and `body-expr' is called with successful combinations aggregated into `results-id' as a list. > first-result : (any . -> . any) If the argument to `first-result' is a backtrackable computation, it is explored to obtain the first success, and that success is returned. If complete backtracking fails, the result is #f. If the argument to `first-result' is any other value, it is returned immediately. ------- Misc ------- > function-reduce* : ((listof red?) any? (any? . -> . boolean?) number? . -> . (listof any?)) A poor-man's `gui' for test interaction. The first argument represents a set of reductions that define a function; the second argument is the starting expression; the third argument is a predicate to recognize ending expressions; the fourth argument limits the number of steps taken. The function applies the reductions repeatedly until an ending expression is found, or until the reduction gets stuck. (If the reduction produces multiple results, an exception is raised.) The result is the list of expressions leading to the ending expression. > unique-names? : ((listof symbol?) . -> . boolean?) Checks a list of symbols, returning #t if each symbol appears only once in the list, #f otherwise. > generate-string : (-> string?) Returns a string that is generated by incrementing a counter. > all-of : (any? (any? . -> . any) . -> . (listof any?)) Takes an arbitrary S-expression as the first argument, and a predicate as the second argument. all-of' then traverses the pairs that make up the S-expression, calling the predicate on every pair and every value within a pair. The result is the list of values for which the predicate returned true. (If the predicate returns true for a pair, the search does not enter the pair.) > transitive-closure : ((listof pair?) . -> . (listof (listof any?)) Given the representation of a function as a list of pairs, `transitive-closure' produces the transitive closure as a list of lists. Each nested list begins with a domain item and continues with a list of range items. Example: (transitive-closure '((a . b) (b . c))) = '((a b c) (b c)) The input function must be complete (i.e., all range elements should be in the domain). Element equality is determined by `eq?' (with the expectation that elements are usually symbols). ====================================================================== The _generator.ss_ module provides a tool for generating instances of a grammar non-terminal. > lang->generator-table : (lang? (listof number?) (listof symbol?) (listof string?) (listof symbol?) number? . -> . generator-table?) Prepares generator information for a particular language, given a set of numbers to use for the `number' keyword, a set of symbols for `variable' and `variable-except', and a set of string for `string'. The fifth argument lists keywords that appear in the grammar but that should be skipped (to limit the generation space). The last argument should be 0, and it is currently ignored. > for-each-generated : (generator-table? (any? number? . -> . any) generator-table? symbol? . -> . any) The first argument is a procedure to call with a series of generated grammar instances and each term's size. Instances are generated from smallest to largest; the size of an instance is roughly the size of the proof tree that demonstrates grammar membership. The second argument is a generator table created with `lang->generator-table'. The third argument is a symbol, the name of a non-terminal for which instances should be generated. > for-each-generated/size : (generator-table? (any? number? . -> . any) generator-table? symbol? . -> . any) Like `for-each-generated', except minimum and maximum sizes are provided, and the order of generation is arbitrary (i.e., some larger instances may be generated before smaller instances).