- Introduction
- Starting
- Using
- Defining Primitives
- Expressing Bracket Abstraction Algorithms
- Interpreter Commands
- Examples
- Downloads
- Building and Installing

This document describes how to build and use `acl` v1.2.

`acl`
("*A*ny *C*ombinatory *L*ogic") interprets
programming languages with similarities to various "Combinatory Logic" (CL)
formal systems. It doesn't interpret any "Combinatory Logic" in that
it runs on computers with finite CPU speed and a finite memory.
Most or all formal systems fail to take these limits into account.

`acl` differs from other interpreters in that the user
must specify any primitives to the interpreter. It has no built-in
atomic primitive combinators.

`acl` does not have any built-in bracket abstraction algorithms.
The user must specify a bracket abstraction algorithm
before doing bracket abstraction on a term. The user must specify primitives
before using them in a bracket abstraction rule definition.

Without specifying any primitives, the interpreter checks the syntax of "applicative structures", where every atomic term constitutes a free variable.

Release 1.2 of `acl` includes
'*^' special symbol in bracket abstraction
specifications.

Another page documents the design and implementation of
a less general Combinatory Logic interpreter.
That document still holds as a good description of the
interpretation of applicative structures that `acl` performs.

After downloading or building the interpreter's executable, you can start it from the command line:

7:57AM 87 % ./acl ACL>

The interpreter uses "`ACL>`" as its prompt for user input.
`acl` has a strict grammar, so you must type in either
a term for reduction,
or an interpreter command,
or a command to examine a term.

A keyboard interrupt (almost always control-C) can interrupt whatever long-running
reduction currently takes place. A keyboard interrupt at the "`ACL>`"
prompt will cause the interpreter to exit.

You have to use keyboard end-of-file (usually control-D) to exit `acl`.

Giving the interpreter a CL term causes it to execute the usual functional language interpreter's read-eval-print loop. The interpreter prints the input in a minimally-parenthesized representation, reduces to normal form, and prints a text representation of the normal form. It prints a prompt, and waits for another user input.

`acl` does standard Combinatory Logic "normal order" evaluation.
The leftmost outermost contraction gets evaluated first.

-c | enable reduction cycle detection |

-d | debug contractions |

-e | elaborate output |

-L filename | Interpret a file named filename before reading user input |

-N number | perform up to number contractions on each input expression. |

-p | Don't print any prompt. |

-s | single-step reductions |

-T number | evaluate an expression for up to number seconds |

-t | trace reductions |

The `-e` or `-s` options have no use without the `-t` option, but `-t` alone
might have some use.

`-L filename` can occur more than one time.

I designed `acl` for use as an interactive system, with a user typing
CL expressions at a prompt. The interpreter reduces the expression
to a normal form (if it has one), or hits some other limit, like a pre-set
timeout, count of allowed contractions or the user's patience.

After entering an entire expression, the user types "return" or "enter" to trigger evaluation.

The built-in prompt for input is the string "`ACL>`".
It appears when the interpreter starts up, or has finished reducing whatever
expression the user gave it last, or it has executed an interpreter command.

You have to type an end-of-file character (almost always control-D) to quit, as it has no built-in "exit" or "quit" command.

A keyboard interrupt (almost always control-C) can interrupt whatever long-running
reduction currently takes place, returning the user to the `ACL>` prompt.
A keyboard interrupt at the "`ACL>`"
prompt will cause the interpreter to exit.

The `-p` command line option causes the interpreter to not print a
prompt. This only has use for non-interactive input. The interpreter
does read from stdin and write to stdout. You can use it as a non-interactive
"filter", with input and output redirection.

Expressions consist of either a single term, or two (perhaps implicitly
parenthesized) terms.
Terms consist of either a
user-defined primitive or a variable, or a parenthesized
expression.
The `reduce` command, and bracket abstraction
each produce an expression.

Variables (which can also serve as abbreviations or names of user-defined primitives) look like C or Java style identifiers. An identifier consists of a letter, followed by zero or more letters or underscores. Variables, abbreviations and user-defined primitives share the same name space. You can't have an abbreviation with the same name as a primitive.

The interpreter treats combinators and variables as "left associative",
the standard in the Combinatory Logic literature.
That means that an expression like this:
`I a b c d` ends up getting treated as though it had parentheses
like this: `((((I a) b) c) d)`

To apply one complex term to another, the user must parenthesize terms.
Applying `W (W K)` to `C W` would look like this:
`(W (W K)) (C W)`.

Users can parenthesize input expressions as much or as little as they desire,
up to the limits of left-association and the meaning they wish to convey
to the interpreter.
The grammar used by `acl` does not
allow single terms inside paired parentheses. It considers strings
like "`(I)`" as syntax errors. You have to put at least two terms
inside a pair of parentheses. Parentheses must have a match.

The interpreter prints out normal forms in minimal-parentheses style. Users have the opportunity to cut-n-paste output back into the input, as output has valid syntax. No keyboard shortcuts exist to take advantage of any previous output.

`acl` does not implement any built-in primitives.
The user must describe desired primitives to the interpreter.

Defining a primitive means getting the interpreter to read a line having this form:

rule:Namen_{i}...->m_{i}...

The ` Name` becomes the primitive's name when used
in a CL expression.

The ` n_{i}` symbols represent ascending-value digits,
beginning with 1. These consitute the required arguments of the primitive
under definition.

The ` m_{i}` symbols also represent digits,
which must also appear
as

- The famous
**S**combinator:`rule: S 1 2 3 -> 1 3 (2 3)` - The obscure
**J**combinator:`rule: J 1 2 3 4 -> 1 3 (2 4 3)` - Smullyan's Mockingbird:
`rule: M 1 -> 1 1` - Half an Ogre:
`rule: HalfOgre 1 2 -> 1 1`. An Ogre (`HalfOgre HalfOgre`), will "eat" all arguments. - The
**K**combinator:`rule: K 1 2 -> 1` - The identity combinator:
`rule: I 1 -> 1`

The interpreter imposes no limits on number of primitives the user defines, or on the number of arguments a given primitive uses or produces. Defining large numbers of primitives will slow it down, as will reducing a primitive that has a large number of arguments, or produces a large number of results in a reduced term.

You can redefine a primitive with another `rule:` input which
uses the name to be redefined. You cannot delete a primitive,
once you have defined it. You have to exit the interpreter to
"delete" pimitives.

At first glance, a user defined primitive and an abbreviation appear redundant. Differences exist, both subtle and gross.

One gross difference: primitives only rearrange, delete, duplicate or regroup their arguments. An abbreviation (in the form of a bracket abstracted expression) can insert primitives. No way to construct an "improper" primitive exists, but abbreviations can do this. Abbreviations can refer to a fixed point combinator. You cannot define a fixed point combinator as a primitive.

`acl` abbreviations work in a way that encourages the
user to build up complex terms by abbreviating smaller,
simpler terms, then invoking all the abbreviations together.
For example, you could build up a fixed-point combinator like this:

ACL> def subexpr (x y x) ACL> def Y ([x,y] subexpr)([y,x] (y subexpr))

This contrived example illustrates the use of abbreviating a common sub-expression, as well as demonstrating that an abbreviation isn't "atomic". The bracket abstractions capture what the user defines as free variables.

A more subtle distinction exists in that primitives reduce atomically: normal-order evaluation doesn't take place "inside" a primitive. An example follows to clarify.

ACL> rule: S 1 2 3 -> 1 3 (2 3) ACL> rule: I 1 -> 1 ACL> def M (S I I) ACL> trace onshow expression after each contractionACL> detect onmark reducible primitives with '*'ACL> step onsingle-step, pause after each contractionACL> M M S I I (S I I) continue?return[2] I* (S I I) (I* (S I I))even out-of-order contractible primitives marked with '*'continue?return[2] S* I I (I* (S I I)) continue?return[4] I* (I* (S I I)) (I* (I* (S I I))) continue?return[3] I* (S I I) (I* (I* (S I I))) continue?return[3] S* I I (I* (I* (S I I))) continue?return[6] I* (I* (I* (S I I))) (I* (I* (I* (S I I)))) continue? qreturnTerminated ACL>

Each abbreviation `M` gets expanded into a term `S I I`.
The `S I I` terms get evaluated in normal order, leftmost-outermost
contraction happens first. This leads to an infinitely expanding term,
not the `M M` → `M M` cycle that one might naively
hope for.

To achieve an `M M` → `M M` cycle, the user
could define `M` as a primitive.

ACL> rule: M 1 -> 1 1 ACL> cycles ondetect cyclical reductionsACL> trace onshow expression after each contractionACL> detect onmark reducible primitives with '*'ACL> M M M M [1] M* M [1] M* M Found a pure cycle of length 1, 1 terms evaluated, ends with ".M M" [1] M* M

"Bracket abstraction" names the process of creating a CL expression without specified variables, that when evaluated with appropriate arguments, ends up giving you the original expression with the specified variables.

The `acl` interpreter uses the conventional square-bracket
notation. For example, to create an expression that will duplicate
its single argument, one would type:

`ACL> [x] x x`

You can use more than one variable inside square brackets, separated with commas:

`ACL> [a, b, c] a (b c)`

The above square-bracketed expression ends up performing three
bracket abstractions, abstracting `c` from `a (b c)`,
`b` from the resulting expression, and `a` from
that expression.

A bracket abstraction makes an expression. You can use abstractions where you might use any other simple or complex expression, defining an abbreviation, a sub-expression of a much larger expression, as an expression to evaluate immediately, or inside another bracket abstraction. For example, you could create Turing's fixed-point combinator like this:

ACL> def U [x][y] (x(y y x)) ACL> def Yturing (U U)

Note the use of nested bracket abstractions. The abstraction of `y`
occurs first, then `x` gets abstracted from the resulting expression.

You could express `[x][y] (x(y y x))`
with the alternate form `[x,y] (x(y y x))`.
The same nested abstraction occurs.

`acl` allows you to express even complicated
bracket abstraction algorithms. You can write rules that match
specific terms, or that match general sub-expressions.

You input the abstraction algorithm in the form of rules, one per line, in decreasing order of priority. Each line has this format:

abstraction: [_]lhs->rhs

The lexical token "`[_]`" denotes the abstraction of whatever
variable the user chooses. It must appear after the `abstraction:`
token. It can also appear in the right-hand-side of an abstraction rule, where
it will cause recursive abstraction(s).

The left-hand-side (*lhs*) looks like a valid CL term, except that it
can contain one or more special symbols, as well as names of primitives
and/or variables:

`_`(underscore) - a "wildcard" for the name of the variable abstracted.`*`(asterisk) - a wildcard meaning "any term, possibly containing the abstracted variable".`*-`(asterisk minus) - a wildcard meaning "any term not containing the abstracted variable".`*!`(asterisk bang) - a wildcard meaning "any term without variables".`*+`(asterisk plus) - a wildcard meaning "any term containing the abstracted variable".`*^`(asterisk bang) - a wildcard meaning "a lexically identical string".

The right-hand-side (*rhs*) also looks like a valid CL term, except that
it can contain one or more special symbols, as well as the names of primitives
and/or variables:

`[_]`- the abstraction-of-whatever-variable again. Triggers a "re-abstraction" of the term constructed by the right-hand-side.`_`(plain old underscore), replaced by the abstracted variable.- Digits,
`1, 2, 3 …`- these stand for terms (including wildcard symbols (`*`,`*-`,`*!`,`*+`)) that appear in the left-hand-side of the rule under consideration. The matching left-hand term gets used in the righ-hand-side.

The order in which the user enters rules amounts to specifying
precedence. The first rule entered at the `ACL>` prompt
has the highest precedence. The last rule entered has the lowest
precedence.

The user must define primitives before using them in a right-hand-side. Otherwise, what appears as a "primitive" will actually constitute a free variable with a confusingly identical name.

The rule format above allows expression of the standard SKI-basis 4-rule algorithm like this:

`abstraction: [_] *- -> K 1``abstraction: [_] _ -> I``abstraction: [_] *- _ -> 1``abstraction: [_] * * -> S ([_] 1) ([_] 2)`

In rule 1, the `*-` on lhs gets into the resulting
expression as the '1' on the right-hand-side.

Similarly, in rule 4, the two expressions on the lhs, each denoted by an '*', have the variable abstracted ('[_]') in the rhs.

The 9-rule bracket abstraction algorithm from John Tromp's Binary Lambda Calculus and Combinatory Logic looks like this:

`abstraction: [_] S K * -> S K``abstraction: [_] *- -> K 1``abstraction: [_] _ -> S K K``abstraction: [_] *- _ -> 1``abstraction: [_] _ * _ -> [_] S S K _ 2``abstraction: [_] (*! (*! *)) -> [_] (S ([_]1) 2 3)``abstraction: [_] (*! * *!) -> ([_] S 1 ([_]3) 2)``abstraction: [_] *! *^ (*! *^) -> [_] (S 1 3 2)``abstraction: [_] * * -> S ([_] 1) ([_] 2)`

Tromp writes rule 5 above as:

λ^{2}x.(x M x) ≡ λ^{2}x.(S S K x M)

The abstracted variable `x` appears in the right- and
left-hand-side of the rule as `_` .
Tromp's `M` appears as `*` on lhs and `2` on
rhs of the `acl` abstraction rule.

The right-hand-side of the rule references the
abstracted-out-variable. The digit `2` in the RHS refers to the
`*` (match any term) marker in the LHS.
The RHS also builds a term which has that variable re-abstracted.

Rules 7 and 9 above feature the '*!' special symbol, which means "term containing no variables whatsoever", a.k.a. a combinator. Tromp seeks to minimize the size of the end result of multi-variable abstractions. Variables exist only to get abstracted away, so a term containing multiple variables will undergo multiple abstractions. Applying rules 6 and 7 to terms more than once ends up making the resulting variable-free terms much larger than they otherwise would end up.

Rule 8 above features the '*^' special symbol. This rule triggers an abstraction when it finds two, lexically-identical sub-expressions, in the positions described. The two expressions in the RHS denoted by '*!' do not get checked for lexical equality, but must not contain variables for the rule to trigger an abstraction.

The "*^" special symbol can appear more than once, should you find it interesting to do so. All sub-trees marked with a '*^' must compare identically to trigger the abstraction rule. More than one abstraction rule can contain '*^' symbols, but the lexical identity only gets checked during examination of a single rule. Lexical identity does not get checked "across rules".

- Defining abbreviations
- Information about expressions
- Detecting reduction cycles
- Intermediate output and single-stepping
- Reduction information and control
- Reading in files
- Printing primitive and abstraction rules

`define`*name**expression*`def`*name**expression*`reduce`*expression*

`define` and `def` allow a user to introduce "abbreviations"
to the input later. Using `define` or `def` causes
`acl` to keep an internal copy of the expression so abbreviated.
When the abbreviation appears in an input expression,
`acl` puts a copy
of the internal-held expression in the input.
No matter how complex the expression, an
abbreviation comprises a single term. Effectively, the interpreter puts
the expanded abbreviation inside a pair of parentheses.

`def` makes an easy-to-type abbreviation of `define`.

The `reduce` command actually
produces an expression, just
like a bracket abstraction. Unlike `define` or `def`,
you can use `reduce` anywhere
an expression would fit, as part of a larger expression, as part of an
abbreviation, or as part of a bracket abstraction.

`reduce` causes an out-of-order (normal order, leftmost outermost)
reduction to take place. The expression next to `reduce`
gets interpreted, and the result put back into the original context.
`reduce` allows a user to store the normal form of an expression,
rather than just a literal expression, as `def` and `define`
don't cause any contractions to take place.

`size`- print the number of atoms in*expression**expression*.`length`- print the number of atoms plus number of applications in*expression**expression*.`print`- print human-readable representation, with abbreviations expanded, but without evaluation.*expression*`printc`- print canonical representation, with abbreviations substituted, but without evaluation.*expression*`redexes`- print a count of possible contractions in*expression**expression*, regardless of order of evaluation.- determine lexical equivalence of any two expressions, after abbreviation substitution, but without evaluation.*expression*=*expression*

`print` lets you see what abbreviations expand to, without evaluation, as does `printc`.
The "=" sign lets you determine *lexical* equality. All combinators, variables and parentheses
have to match as strings, otherwise "=" deems the expressions not equivalent.
You can put in explicit `reduce` commands on both sides of an "=",
otherwise, no evaluation takes place.

`size` and `length` seem redundant, but authorities
measure CL expressions different ways. These two methods should cover the
vast majority of cases.

The `printc` command, and cycle detection output
use a canonical form of representing a CL expression.

In this case, "canonical" means: pre-order, depth-first, left-to-right traversal of the parse tree, output of a period (".") for each application, and a space before each combinator or variable.

A simple application (`K I`, for example) looks like this: `.K I`

A more complex application (`P R (Q R)`) looks like this: `..P R.Q R`

The advantage to this sort of notation is that every application appears explicitly, and variant, semantically-equivalent parentheses don't appear.

`cycles on|off``detect on|off`

Some CL expressions end up creating a cycle. `M M` or `W W W`
constitute examples from common bases.
After a certain number of
contractions, the interpreter encounters an expression it has previously created.
If you issue the `cycles on` command, the interpreter keeps track of
every expression in the current reduction, and stops when it detects a cyclical
reduction.

`detect on` causes the interpreter to count and mark possible contraction (with an asterisk),
regardless of reduction order. It does "normal order" reduction, but ignores that
for the contraction count. This has use with `trace on`.

Turning cycle detection on will add time to an expression's reduction, as will possible contraction detection.

`trace on|off``elaborate on|off``debug on|off``step on|off`

You can issue any of these commands without an "on" or "off" argument to inquire about the current state of the directive.

`trace`, `debug` and `elaborate` provide
increasingly verbose output. `trace` shows the expression
after each contraction, `debug` adds information about which
branch of an application it evaluates, and `elaborate`
adds to that information useful in debugging memory allocation problems.

`detect` causes `trace` to also print a count
of possible contractions (not all of them normal order reductions),
and mark contractable primitives with an asterisk.

`step on` causes the interpreter to stop after each contraction,
print the intermediate expression, and wait, at a `?` prompt
for user input. Hitting return goes to the next contraction, `n`
or `q` terminates the reduction, and `c` causes it
to quit single-stepping.

`timer on|off`- turn on/off per-reduction elapsed time output.`timeout 0|N`- stop reducing after`N`seconds.`count 0|N`- stop reducing after`N`contractions.

You can turn time outs off by using a 0 (zero) second timeout. Similarly, you can turn contraction-count-limited evaluation off with a 0 (zero) count.

`timer on` also times bracket abstraction.

`load "`*filename*"

You have to double-quote filenames with whitespace or
non-alphanumeric characters in them.
You can use absolute filenames (beginning with "/") or you can use
filenames relative to the current working directory of the `acl`
process.

`rules`- prints out what rules about primitives it has.`abstractions`- prints out what abstraction rules it has.

Rules about primitives and abstraction rules should appear in a format appropriate for cut-n-paste, that is, in input syntax. Abstraction rules should appear in order of precedence, highest precedence first.

The following examples demonstrate either an interesting facet of Combinatory Logic (Klein fourgroup, Grzegorzyk bracket abstraction, AMEN basis) or illustrates part of the interpreter that I found hard to describe (abstraction rules in Tromp's bracket abstraction, and Scott Numerals example). Your tastes may vary.

- SKI Basis, with "abcf" bracket abstraction.
- SKI Basis, with John Tromp's bracket abstraction.
- BWCK Basis, Grzegorzyk bracket abstraction.
- IJ Basis (λI basis), including bracket abstraction.
- IBCS Basis (λI basis), with adequate Church Numerals.
- BTMK Basis, with home-grown bracket abstraction.
- SNC Basis, with home-grown bracket abstraction.
- AMEN Basis, another home-grown bracket abstraction algorithm.
- OAME Basis, bracket abstraction similar to AMEN basis.
- Scott Numerals in an 8-combinator basis, and a 9-rule abstraction algorithm.
- D Numerals, where
`[x] x`represents 0,`[x]x x`represents 1, … - Klein fourgroup, with application as the operator.

- RPM package for x86 linux
- Arch package for i686 linux
- Slackware package for x86 linux
- Plain old x86 Linux executable
- Source code

I developed this program on Slackware Linux 12.0, and Arch Linux. I used the C compilers GCC, LCC, PCC, TCC and Clang. I tried to make it as ANSI-C as possible by using various compilers, not allowing any compiler warnings when building, and using as many warning flags for GCC compiles as possible.

I expect it will build on any *BSD-based system, but I haven't
done that formally. For a BSD system, try `make cc`.

Licensed under GNU Public License v2, or later.

- Get the source code. You can:
- Download via your browser.
- Download using wget:
`wget http://www.stratigery.com/acl/acl-1.2.tar.gz`

- Unpack source code. From the command line:
`tar xzf acl-1.2.tar.gz` - Change directory. From the command line:
`cd acl-1.2` - Compile source code. From the command line:
`make gnu`- for almost every linux distro.`make cc`- for BSDs and Solaris.

`make cc`uses traditionally-unix-named tools, and may work better on Solaris or the BSDs. Traditional, AT&T derived`lex`will require a minor edit to file`lex.l`. You can see what to uncomment at the very top of the file. - At this point, you can test the newly-compiled executable.
From the command line:
`./runtests`. Most of the tests should run quite rapidly, in under a second. At least two of the tests run for 30 seconds or so, and at least one of the tests provokes a syntax error message from the interpreter. - Install the interpreter wherever you want, or you can execute it in-place.
To install, use the
`cp`or`mv`commands to move or copy the executable to where ever you want it. It does not care what directory it resides in, and it does not look for configuration files anywhere.

I'm pretty sure that {S, N} is not a basis, less sure that {S, N, T} is not a basis.

rule: C 1 2 3 -> 1 3 2 rule: N 1 2 -> 2 rule: S 1 2 3 -> 1 3 (2 3) abstraction: [_] *- -> C N 1 abstraction: [_] _ -> N N abstraction: [_] *- _ -> 1 abstraction: [_] * * -> S ([_] 1) ([_] 2) ([a,b,c] a c b) first second third ([a,b,c] a (b c)) first second third ([a,b] a b b) first second

rule: B 1 2 3 -> 1 (2 3) rule: W 1 2 -> 1 2 2 rule: C 1 2 3 -> 1 3 2 rule: N 1 2 -> 2 abstraction: [_] *- -> C N 1 abstraction: [_] _ -> N N abstraction: [_] *- _ -> 1 abstraction: [_] *- *+ -> B 1 ([_]2) abstraction: [_] *+ *- -> C ([_]1) 2 abstraction: [_] * * -> W (B (C ([_]1)) ([_]2))acl-1.2/examples/ski.basis0000664000076400007640000000055111404223440015333 0ustar bedigerbediger# The usual {S, K, I} basis. # $Id: ski.basis,v 1.3 2010/06/10 17:55:44 bediger Exp $ rule: I 1 -> 1 rule: K 1 2 -> 1 rule: S 1 2 3 -> 1 3 (2 3) # Hindley & Seldin's Def 2.14 abstraction: [_] *- -> K 1 abstraction: [_] _ -> I abstraction: [_] *- _ -> 1 abstraction: [_] * * -> S ([_] 1) ([_] 2) S (S K K) (S K K) x ([x] x x x) a [x] x [x,y] x [p,q,r] p r (q r) acl-1.2/examples/tromp.abstraction0000664000076400007640000000224111604665705017134 0ustar bedigerbediger# The {S, K} basis, but with John Tromp's # 9-rule abstraction algoritm. # $Id: tromp.abstraction,v 1.4 2011/07/05 19:53:41 bediger Exp $ rule: K 1 2 -> 1 rule: S 1 2 3 -> 1 3 (2 3) # abstraction: [_] S K * -> S K abstraction: [_] *- -> K 1 abstraction: [_] _ -> S K K abstraction: [_] *- _ -> 1 abstraction: [_] _ * _ -> [_] S S K _ 2 abstraction: [_] (*! (*! *)) -> [_] (S ([_]1) 2 3) abstraction: [_] (*! * *!) -> [_] (S 1 ([_]3) 2) abstraction: [_] *! *^ (*! *^) -> [_] (S 1 3 2) abstraction: [_] * * -> S ([_] 1) ([_] 2) ([x] x x x) a # This bracket abstraction: [y,x] y (x y x) # comes out quite differently with H&S's Rule 2.18 # Tromp comes up with: # S (K (S S (S (S S K)))) K # Rule 2.18: (this rule assmes {S, K, I} basis) # S (S (K S) K) (S (S (K S) (S (K (S I)) K)) (K I)) # Test the abstraction: [y,x] y (x y x) = S (K (S S (S (S S K)))) K # The first rule ([x] S K M -> S K for any M) causes # some problems for direct, lexical comparisons of # even the results of applying an abstracted expression # to some arguments. The first rule can give you # extensionally equivalent expressions, like S K and K I # are for weak CL reduction, that aren't lexically identical. acl-1.2/examples/ibcs.basis0000664000076400007640000000320511375622360015477 0ustar bedigerbediger# {I, B, C, S} basis for lambda-I calculus # $Id: ibcs.basis,v 1.3 2010/05/22 00:27:28 bediger Exp $ rule: B 1 2 3 -> 1 (2 3) rule: C 1 2 3 -> 1 3 2 rule: I 1 -> 1 rule: S 1 2 3 -> 1 3 (2 3) # No rule [x] M -> K M, as no K combinator exists in this basis. abstraction: [_] _ -> I abstraction: [_] *- *+ -> B 1 ([_] 2) abstraction: [_] *+ *- -> C ([_] 1) 2 abstraction: [_] * * -> S ([_] 1) ([_] 2) def U ([x,y] y (x x y)) # Arithmetic operators from: # "A Theory of Positive Integers in Formal Logic, Part 1" # S.C. Kleene, American Journal of Mathematics, vol 57, no 1, Jan. 1935 define c1 ([f,n] f n) define succ ([r,f,x] (f (r f x))) def c2 (reduce succ c1) def c3 (reduce succ c2) reduce c2 f n = f (f n) reduce c3 f n = f (f (f n)) define plus ([r,s,f,x] (r f (s f x))) define mult ([r,s,x] (r (s x))) # B by any other name define exp [m,n] n m # Number dyads and triads def D ([r,s,f,g,a] (r f (s g a))) def D1 ([r,f] r f I) def D2 ([r] r I) reduce (D2 (D (succ c1) c1) f n) = f n reduce (D1 (D (succ (succ c1)) c1) f n) = f (f (f n)) def T ([r,s,t,f,g,h,a] (r f (s g (t h a)))) def T1 ([r,f] (r f I I)) def T2 ([r,f] (r I f I)) def T3 ([r] (r I I)) (reduce (T1 (T c1 c2 c3) f n)) = f n (reduce (T2 (T c1 c2 c3) f n)) = f (f n) (reduce (T3 (T c1 c2 c3) f n)) = f (f (f n)) # predecessor in lambda-I! def F ([r] (T (T2 r) (T3 r) (succ (T3 r)))) def ff (T c1 c1 c1) def pred ([r] (T1 (r F ff))) (reduce pred c3 f n) = f (f n) (reduce pred c2 f n) = f n (reduce pred c1 f n) = f n # Subtraction def sub ([u,v] (v pred u)) (reduce sub c3 c2 f n) = (reduce c1 f n) (reduce sub (succ c3) c2 f n) = (reduce c2 f n) (reduce sub c1 c1 f n) = (reduce c1 f n) acl-1.2/examples/bwck.basis0000664000076400007640000000065711375622360015515 0ustar bedigerbediger# {B, W, C, K} basis # $Id: bwck.basis,v 1.2 2010/05/22 00:27:28 bediger Exp $ rule: B 1 2 3 -> 1 (2 3) rule: C 1 2 3 -> 1 3 2 rule: W 1 2 -> 1 2 2 rule: K 1 2 -> 1 # Grzegorzyk "G" algorithm, abstraction: [_] *- -> K 1 abstraction: [_] _ -> W K abstraction: [_] *- _ -> 1 abstraction: [_] *- *+ -> B 1 ([_]2) abstraction: [_] *+ *- -> C ([_]1) 2 abstraction: [_] * * -> W (B (C ([_]1)) ([_]2)) def S ([p,q,r] p r (q r)) S a b c acl-1.2/examples/oame.basis0000664000076400007640000000113311375622360015476 0ustar bedigerbediger# Soren Stenlund's arithmetic basis, OAME # $Id: oame.basis,v 1.2 2010/05/22 00:27:28 bediger Exp $ rule: O 1 2 -> 2 # Also N, in Hancock's AMEN basis rule: A 1 2 3 4 -> 1 3 (2 3 4) rule: M 1 2 3 -> 1 (2 3) # B in any other basis rule: E 1 2 -> 2 1 # T in any other basis abstraction: [_] *- -> M (E 1) O abstraction: [_] _ -> O O abstraction: [_] *- _ -> 1 abstraction: [_] *- *+ -> M 1 ([_]2) abstraction: [_] *+ *- -> M (E 2) ([_]1) abstraction: [_] * * -> M (E ([_]2)) (A ([_]1) E) def S ([p,q,r] p r (q r)) S a b c cycles on detect on # pure cycle trace on A E (A E)(A E) (A E) acl-1.2/examples/fourgroup0000664000076400007640000001066011375622360015512 0ustar bedigerbediger# Klein Fourgroup, with CL application as the operation. # Some of this is from Henk Barendregdt's 1988 "Juggling With # Combinators": http://repository.ubn.ru.nl/handle/2066/17290 # # The idea is to embed a fourgroup into CL expressions. When # you apply the CL expressions, you end up performing the # operation of the fourgroup on members of the fourgroup. # The Fourgroup here is {1,3,5,7}, operation multiplication modulo 8, # but I'm going to do it by table lookup instead of using an # operation (modulo) that doesn't have an (easy) normal form # Scott Numerals as per March 8, 2006 revision of John Tromp's # paper, "Binary Lambda Calculus and Combinatory Logic". # "The Scott Numerals [i] can be used to define arithmetic, as # well as for indexing lists; M [i] select's the i'th element # of a sequence M." # $Id: fourgroup,v 1.2 2010/05/22 00:27:28 bediger Exp $ # Basis: SKI rule: I 1 -> 1 rule: K 1 2 -> 1 rule: S 1 2 3 -> 1 3 (2 3) # Hindley & Seldin's Def 2.14 abstraction: [_] *- -> K 1 abstraction: [_] _ -> I abstraction: [_] *- _ -> 1 abstraction: [_] * * -> S ([_]1) ([_]2) #define zero %a.%b.a def zero [a][b] a #define succ %c.%d.%e.(e c) define succ [c][d][e] e c #define case %f.%g.%h.f g h define case [p][q][r]p q r #define pred %i.(i (%j.%k.j) (%l.l)) define pred [i] (i ([j][k]j) ([l]l)) def True ([x][y] x) def False ([x][y] y) def nil False def sn0 True def sn1 (reduce succ sn0) def sn2 (reduce succ sn1) def sn3 (reduce succ sn2) def sn4 (reduce succ sn3) # Original "Cayley table" of a fourgroup: # * 1 3 5 7 # \ _______ # 1 |1 3 5 7 # 3 |3 1 7 5 # 5 |5 7 1 3 # 7 |7 5 3 1 # # Since this example has a table-driven function, with # Scott numerals as arguments, and selectors of elements # of lists, why not use 0,1,2,3 for 1,3,5,7? # * 0 1 2 3 # \ _______ # 0 |0 1 2 3 # 1 |1 0 3 2 # 2 |2 3 0 1 # 3 |3 2 1 0 # # More abstractly: # * a b c d # \ _______ # a |a b c d # b |b a d c # c |c d a b # d |d c b a # # You could certainly do a fourgroup with an operation # like: # greater-than # def g2 [w] [a] [b] zerotest a f (zerotest b t (w w (pred a) (pred b))) # def g g2 g2 # modulo 8 # def A1 [w][x] g x b7 (w w (monus x b8)) x # def modulo8 (A1 A1) # Mulitplication modulo 8 # def F [a][b] modulo8 (mult a b) # # And then you'd have a fourgroup of {sn1, sn3, sn5, sn7}, with F # (not mere application) as the group operation. Also, modulo # as defined above doesn't have a normal form, so the Ma, Mb,... # combinators wouldn't have a normal form, and the "=" comparisons # wouldn't be nearly so neat. # Make lists representing the horizontal rows, then # make a list of those lists. # pair combinator, Smullyan's "Vireo" bird. define pair ([p][q][z] z p q) define L0 (reduce pair sn0 (pair sn1 (pair sn2 (pair sn3 nil)))) define L1 (reduce pair sn1 (pair sn0 (pair sn3 (pair sn2 nil)))) define L2 (reduce pair sn2 (pair sn3 (pair sn0 (pair sn1 nil)))) define L3 (reduce pair sn3 (pair sn2 (pair sn1 (pair sn0 nil)))) define matrix (reduce pair L0 (pair L1 (pair L2 (pair L3 nil)))) # F, a combinator, represents the function f(x,y). # In this case, a table lookup using the list selector property of # Scott Numerals. define F [x][y] matrix y x # note that G has the "pair" functionality built in. define G [p][q][r][z] z p (F r q) # The elements of the fourgroup def Ma (reduce pair G sn0) def Mb (reduce pair G sn1) def Mc (reduce pair G sn2) def Md (reduce pair G sn3) # So Ma =

#
# Ma Ma =