Skip to content

Instantly share code, notes, and snippets.

@VictorTaelin
Last active February 26, 2025 03:05
Show Gist options
  • Save VictorTaelin/182a60849960a158c8f6d349ced9c182 to your computer and use it in GitHub Desktop.
Save VictorTaelin/182a60849960a158c8f6d349ced9c182 to your computer and use it in GitHub Desktop.
yet another FP prompt

The Interaction Calculus

The Interaction Calculus (IC) is term rewriting 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;
&L{(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)


TASK: complete the "Example 4" reduction above. write it in a single code block, similarly to the other examples.
@VictorTaelin
Copy link
Author

The goal is to normalize an overlab example based on the Interaction Calculus tutorial. The correct reduction is:

({λ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 }  

Note that arriving at the right answer isn't sufficient. The AI needs to derive the correct steps, in the correct order. The challenging bit is the DUP-LAM pass, which entangles the λz.z as λr.f0 and λs.f1, superposing its bound variables as {r,s}. Getting this pass right implies the AI understood the Interaction Calculus.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment