Last active
March 7, 2025 02:49
-
-
Save VictorTaelin/57b462808f8f0aad720fa63a56e9548f to your computer and use it in GitHub Desktop.
Interaction Calculus .prompt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# The Interaction Calculus | |
The Interaction Calculus (IC) is term rewritting system inspired by the Lambda | |
Calculus (λC), but with some major differences: | |
1. Vars are affine: they can only occur up to one time. | |
2. Vars are global: they can occur anywhere in the program. | |
3. There is a new core primitive: the superposition. | |
An IC term is defined by the following grammar: | |
``` | |
Term ::= | |
| VAR: Name | |
| LAM: "λ" Name "." Term | |
| APP: "(" Term " " Term ")" | |
| SUP: "{" Term "," Term "}" | |
| DUP: "!" "{" Name "," Name "}" "=" Term ";" Term | |
``` | |
Where: | |
- VAR represents a named variable. | |
- LAM represents a lambda. | |
- APP represents a application. | |
- SUP represents a superposition. | |
- DUP represents a duplication. | |
Lambdas are curried, and work like their λC counterpart, except with a relaxed | |
scope, and with affine usage. Applications eliminate lambdas, like in λC, | |
through the beta-reduce (APP-LAM) interaction. | |
Superpositions work like pairs. Duplications eliminate superpositions through | |
the collapse (DUP-SUP) interaction, which works exactly like a pair projection. | |
What makes SUPs and DUPs unique is how they interact with LAMs and APPs. When a | |
SUP is applied to an argument, it reduces through the overlap interaction | |
(APP-SUP), and when a LAM is projected, it reduces through the entangle | |
interaction (DUP-LAM). This gives a computational behavior for every possible | |
interaction: there are no runtime errors on IC. | |
The interaction rules are defined below: | |
Beta-Reduce: | |
``` | |
(λx.f a) | |
-------- APP-LAM | |
x <- a | |
f | |
``` | |
Overlap: | |
``` | |
({a,b} c) | |
--------------- APP-SUP | |
! {x0,x1} = c; | |
{(a x0),(b x1)} | |
``` | |
Entangle: | |
``` | |
! {r,s} = λx.f; | |
K | |
--------------- DUP-LAM | |
r <- λx0.f0 | |
s <- λx1.f1 | |
x <- {x0,x1} | |
! {f0,f1} = f; | |
K | |
``` | |
Collapse: | |
``` | |
! {x,y} = {a,b}; | |
K | |
--------------- DUP-SUP | |
x <- a | |
y <- b | |
K | |
``` | |
Where `x <- t` stands for a global substitution of `x` by `t`. | |
Since variables are affine, substitutions can be implemented efficiently by just | |
inserting an entry in a global substitution map (`sub[var] = value`). There is | |
no need to traverse the target term, or to handle name capture, as long as fresh | |
variable names are globally unique. It can also be implemented in a concurrent | |
setup with a single atomic-swap. | |
Below is a pseudocode implementation of these interaction rules: | |
``` | |
def app_lam(app, lam): | |
sub[lam.nam] = app.arg | |
return lam.bod | |
def app_sup(app, sup): | |
x0 = fresh() | |
x1 = fresh() | |
a0 = App(sup.fst, Var(x0)) | |
a1 = App(Sup.snd, Var(x1)) | |
return Dup(x0, x1, app.arg, Sup(a0, a1)) | |
def dup_lam(dup, lam): | |
x0 = fresh() | |
x1 = fresh() | |
f0 = fresh() | |
f1 = fresh() | |
sub[dup.fst] = Lam(x0, Var(f0)) | |
sub[dup.snd] = Lam(x1, Var(f1)) | |
sub[lam.nam] = Sup(Var(x0), Var(x1)) | |
return Dup(f0, f1, lam.bod, dup.bod) | |
def dup_sup(dup, sup): | |
sub[dup.fst] = sup.fst | |
sub[dup.snd] = sup.snd | |
return dup.bod | |
``` | |
Terms can be reduced to weak head normal form, which means reducing until the | |
outermost constructor is a value (LAM, SUP, etc.), or until no more reductions | |
are possible. Example: | |
``` | |
def whnf(term): | |
while True: | |
match term: | |
case Var(nam): | |
if nam in sub: | |
term = sub[nam] | |
else: | |
return term | |
case App(fun, arg): | |
fun = whnf(fun) | |
match fun: | |
case Lam(_, _): | |
term = app_lam(term, fun) | |
case Sup(_, _): | |
term = app_sup(term, fun) | |
case _: | |
return App(fun, arg) | |
case Dup(fst, snd, val, bod): | |
val = whnf(val) | |
match val: | |
case Lam(_, _): | |
term = dup_lam(term, val) | |
case Sup(_, _): | |
term = dup_sup(term, val) | |
case _: | |
return Dup(fst, snd, val, bod) | |
case _: | |
return term | |
``` | |
Terms can be reduced to full normal form by recursively taking the whnf: | |
``` | |
def normal(term): | |
term = whnf(term) | |
match term: | |
case Lam(nam, bod): | |
bod_nf = normal(bod) | |
return Lam(nam, bod_nf) | |
case App(fun, arg): | |
fun_nf = normal(fun) | |
arg_nf = normal(arg) | |
return App(fun_nf, arg_nf) | |
case Sup(fst, snd): | |
fst_nf = normal(fst) | |
snd_nf = normal(snd) | |
return Sup(fst_nf, snd_nf) | |
case Dup(fst, snd, val, bod): | |
val_nf = normal(val) | |
bod_nf = normal(bod) | |
return Dup(fst, snd, val_nf, bod_nf) | |
case _: | |
return term | |
``` | |
Below are some normalization examples. | |
Example 0: (simple λ-term) | |
``` | |
(λx.λt.(t x) λy.y) | |
------------------ APP-LAM | |
λt.(t λy.y) | |
``` | |
Example 1: (larger λ-term) | |
``` | |
(λb.λt.λf.((b f) t) λT.λF.T) | |
---------------------------- APP-LAM | |
λt.λf.((λT.λF.T f) t) | |
----------------------- APP-LAM | |
λt.λf.(λF.t f) | |
-------------- APP-LAM | |
λt.λf.t | |
``` | |
Example 2: (global scopes) | |
``` | |
{x,(λx.λy.y λk.k)} | |
------------------ APP-LAM | |
{λk.k,λy.y} | |
``` | |
Example 3: (superposition) | |
``` | |
!{a,b} = {λx.x,λy.y}; (a b) | |
--------------------------- DUP-SUP | |
(λx.x λy.y) | |
----------- APP-LAM | |
λy.y | |
``` | |
Example 4: (overlap) | |
``` | |
({λx.x,λy.y} λz.z) | |
------------------ APP-SUP | |
! {x0,x1} = λz.z; {(λx.x x0),(λy.y x1)} | |
--------------------------------------- DUP-LAM | |
! {f0,f1} = {r,s}; {(λx.x λr.f0),(λy.y λs.f1)} | |
---------------------------------------------- DUP-SUP | |
{(λx.x λr.r),(λy.y λs.s)} | |
------------------------- APP-LAM | |
{λr.r,(λy.y λs.s)} | |
------------------ APP-LAM | |
{λr.r,λs.s} | |
``` | |
--- | |
TODO: based on the spec above, create a complete TypeScript implementation of | |
the Interaction Calculus. Include a stringifier, a parser, and an evaluator. It | |
must work as a CLI tool that receives a IC term as the argument, and outputs its | |
normal form, as well as the interaction count. Make the parser single-pass and | |
recursive, without a separate tokenizer. Use JSON to represent ADTs, as in | |
`{$:"Cons",head,tail}`. Make the file concise, simple and purely functional, | |
except for the fresh count, interaction count, and the subst map, which can be | |
global states. Answer below with a complete TS implementation of the IC. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sonnet-3.7 generation:
Grok-3 generation:
Test with:
Should output a term equivalent to
λa.λb.a
, with an interaction count of 16.