Giter VIP home page Giter VIP logo

interaction-calculus's Introduction

Interaction Calculus

The Interaction Calculus (IC) is a minimal programming language and model of computation obtained by "completing" the affine Lambda Calculus in a way that matches perfectly Lamping's optimal reduction algorithm. It can also be seen as a textual syntax for Symetric Interaction Combinators: both views are equivalent. As a model of computation, the IC has compeling characteristics:

  1. It features higher-order functions, just like the Lambda Calculus.

  2. It has a well-defined cost model, just like the Turing Machine.

  3. It is inherently concurrent, making it prone to massive parallelism.

  4. It is fully linear, making it garbage-collection free.

This repository contains a Rust reference implementation. Also check:

Usage

1. Install

git clone https://github.com/victortaelin/interaction_calculus
cd interaction_calculus
cargo install --path .

2. Create a 'main.ic' file

def id = λx x
def c2 = λf λx (dup #b f0 f1 = f; (f0 (f1 x)))
(c2 id)

3. Run it

ic main.ic

See example.ic for a larger example.

Language

Interaction Calculus terms are defined by the following grammar:

term ::=
  | λx term                   -- abstraction
  | (term term)               -- application
  | {term term}#N             -- superposition
  | dup #N {p q} = term; term -- duplication
  | x                         -- variable

Where variable have global scope (can occur outside binding lambdas).

The IC has 4 primitive reduction rules:

((λx f) a)
---------- lambda application
x <- a
f

({u v}#i a)
---------------- superposition application
dup #i x0 x1 = a
{(u x0) (v x1)}#i

dup #i p q = λx f
body
----------------- lambda duplication
p <- λx0 r
q <- λx1 s
x <- {x0 x1}#i
dup #i r s = f
body

dup #i p q = {r s}#j
body
-------------------- superposition duplication
if #i == #j:
  a <- fst
  b <- snd
  cont
else:
  a <- {a0 a1}#j
  b <- {b0 b1}#j
  dup #i a0 a1 = fst;
  dup #i b0 b1 = snd;
  cont

Where, a <- b stands for a global, linear substitution of a by b. It can be performed in O(1) by a simple array write, which, in turn, makes all rewrite rules above O(1) too.

And that's all!

Why?

Consider the conventional Lambda Calculus, with pairs. It has two computational rules:

  • Lambda Application : (λx body) arg

  • Pair Projection : let {a b} = {fst snd} in cont

When compiling the Lambda Calculus to Interaction Combinators:

  • lams and apps can be represented as constructor nodes (γ)

  • pars and lets can be represented as duplicator nodes (δ)

As such, lambda applications and pair projections are just annihilations:

      Lambda Application                 Pair Projection
                                                                   
      (λx body) arg                      let {a b} = {fst snd} in cont 
      ----------------                   -----------------------------
      x <- arg                           a <- fst                  
      body                               b <- snd                  
                                         cont                      
                                                                   
    ret  arg    ret  arg                  b   a       b    a       
     |   |       |    |                   |   |       |    |       
     |___|       |    |                   |___|       |    |       
 app  \ /         \  /                let  \#/         \  /        
       |    ==>    \/                       |    ==>    \/         
       |           /\                       |           /\         
 lam  /_\         /  \               pair  /#\         /  \        
     |   |       |    |                   |   |       |    |       
     |   |       |    |                   |   |       |    |       
     x  body     x   body                fst snd    fst   snd      
                                                                   
 "The application of a lambda        "The projection of a pair just 
 substitutes the lambda's var        substitutes the projected vars
 by the application's arg, and       by each element of the pair, and
 returns the lambda body."           returns the continuation."

But annihilations only happen when identical nodes interact. On interaction nets, it is possible for different nodes to interact, which triggers another rule, the commutation. That rule could be seen as handling the following expressions:

  • Lambda Projection : let {a b} = (λx body) in cont

  • Pair Application : ({fst snd} arg)

But how could we "project" a lambda or "apply" a pair? On the Lambda Calculus, these cases are undefined and stuck, and should be type errors. Yet, by interpreting the effects of the commutation rule on the interaction combinator point of view, we can propose a reasonable reduction for these lambda expressions:

   Lambda Application                         Pair Application
                                                                  
   let {a b} = (λx body) in cont             ({fst snd} arg)   
   ------------------------------             ---------------
   a <- (λx0 b0)                             let {x0 x1} = arg in
   b <- (λx1 b1)                             {(fst x0) (snd x1)}
   x <- {x0 x1}
   let {b0 b1} = body in
   cont                   
       
    ret  arg         ret  arg            ret  arg         ret  arg  
     |   |            |    |              |   |            |    |   
     |___|            |    |              |___|            |    |   
 let  \#/            /_\  /_\         app  \ /            /#\  /#\  
       |      ==>    |  \/  |               |      ==>    |  \/  |  
       |             |_ /\ _|               |             |_ /\ _|  
 lam  /_\            \#/  \#/        pair  /#\            \ /  \ /  
     |   |            |    |              |   |            |    |   
     |   |            |    |              |   |            |    |   
     x  body          x   body           var body         var  body 

 "The projection of a lambda         "The application of a pair is a pair
 substitutes the projected vars      of the first element and the second
 by a copies of the lambda that      element applied to projections of the
 return its projected body, with     application argument."
 the bound variable substituted
 by the new lambda vars paired."

This, in a way, completes the lambda calculus; i.e., previously "stuck" expressions now have a meaningful computation. That system, as written, is Turing complete, yet, it is very limited, since it isn't capable of cloning pairs, or cloning cloned lambdas. There is a simple way to greatly increase its expressivity, though: by decorating lets with labels, and upgrading the pair projection rule to:

let #i{a,b} = #j{fst,snd} in cont
---------------------------------
if #i == #j:
  a <- fst
  b <- snd
  cont
else:
  a <- #j{a0,a1}
  b <- #j{b0,b1} 
  let #i{a0,a1} = fst in
  let #i{b0,b1} = snd in
  cont

That is, it may correspond to either an Interaction Combinator annihilation or commutation, depending on the value of the labels #i and #j. This makes IC capable of cloning pairs, cloning cloned lambdas, computing nested loops, performing Church-encoded arithmetic up to exponentiation, expressing arbitrary recursive functions such as the Y-combinators and so on. In other words, with this simple extension, IC becomes extraordinarily powerful and expressive, giving us:

  1. A new model of computation that is similar to the lambda calculus, yet, can be reduced optimally.

  2. A general purpose, higher-order "core language" that is lighter and faster than the lambda calculus.

  3. A term-based view for interaction combinators, making it easier to reason about their graphs.

That said, keep in mind the IC is not equivalent to the Lambda Calculus. It is a different model. There are λ-terms that IC can't compute, and vice-versa. For example, the Lambda Calculus can perform self-exponentiation of church-nats as λx (x x), which isn't possible on IC. Yet, on IC, we can have call/cc, direct O(1) queues, and fully linear HOAS, which aren't possible on the Lambda Calculus.

Finally, note that, in order to differentiate IC's "pairs" and "lets" from their λ-Calculus counterparts, which behave differently, we call them "sups" and "dups", respectivelly.

Examples

Lambda-application and superposition-projection (same as pair-projection).

λu λv dup {a b} = {(λx x) (λy y)}; {(a u) (b v)}
------------------------------------------------ superposition-projection
λu λv {((λx x) u) ((λy y) v)}
----------------------------- lambda-application
λu λv {((λx x) u) v}
-------------------- lambda-application
λu λv {u v}

Using lambda-projection to copy a function.

dup {a b} = λx λy λz y; {a b}
----------------------------- lambda-projection
dup {a b} = λy λz y; {(λx0 a) (λx1 b)}
-------------------------------------- lambda-projection
dup {a b} = λz {y0 y1}; {(λx0 λy0 a) (λx1 λy1 b)}
------------------------------------------------- lambda-projection
dup {a b} = {y0 y1}; {(λx0 λy0 λz0 a) (λx1 λy1 λz1 b)}
------------------------------------------------------ superposition-projection
{(λx0 λy0 λz0 y0) (λx1 λy1 λz1 y1)}

Demonstrating superposition-application (not part of Lambda Calculus)

{{(λx x) (λy y)} (λt t)}
------------------------ superposition-application
dup {a0 a1} = λt t; {((λx x) a0) ((λy y) a1)}
--------------------------------------------- lambda-projection
dup {a0 a1} = {t0 t1}; {((λx x) (λt0 a0)) ((λy y) (λt1 a1))}
------------------------------------------------------------ superposition-projection
{((λx x) (λt0 t0)) ((λy y) (λt1 t1))}
------------------------------------- lambda-application
{((λx x) (λt0 t0)) (λt1 t1)}
---------------------------- lambda-application
{(λt0 t0) (λt1 t1)}

Example 3: 2 + 3.

This is equivalent to:

data Nat = S Nat | Z

add : Nat -> Nat -> Nat
add (S n) m = S (add n m)
add Z     m = m

main : Nat
main = add (S (S (S Z))) (S (S Z))

Full reduction.

Example 4: applying not 8 times to True.

Full reduction.

Here is a handwritten reduction of 2^(2^2).

High-order Virtual Machine (HVM)

The High-order Virtual Machine (HVM) is a high-performance practical implementation of the IC. Check it out!

interaction-calculus's People

Contributors

derenash avatar kings177 avatar victortaelin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

interaction-calculus's Issues

Naming

If anyone has a suggestion for a better name (since Abstract Calculus seems to be impopular) please let me know. Symmetric Lambda Calculus (since it is isomorphic to Symmetric Interaction Combinators) may be a good option. Thoughts?

Some programs make it hang/loop forever

This program makes it hang forever:

def Z = λs λz (z)
def S = λn λs λz dup #s s1 s2 = s; (s1 (n s2 z))
def pow = λa λb (b a)
(pow (S (S Z)) (S (S Z)))

I added a println!("{}", from_net(&inet)); after the call to rewrite, then it prints:

dup #s e f = c; dup #s j k = h; dup #s p q = n; dup #s u v = s; (λa (a (λb λc λd (e ((b f) d)) (λg λh λi (j ((g k) i)) λ* λl l))) (λm λn λo (p ((m q) o)) (λr λs λt (u ((r v) t)) λ* λw w)))
dup #s d e = b; dup #s i j = g; dup #s o p = m; dup #s t u = r; ((λa λb λc (d ((a e) c)) (λf λg λh (i ((f j) h)) λ* λk k)) (λl λm λn (o ((l p) n)) (λq λr λs (t ((q u) s)) λ* λv v)))
dup #s c j = a; dup #s g h = e; dup #s n o = l; dup #s s t = q; (λa λb (c (((λd λe λf (g ((d h) f)) λ* λi i) j) b)) (λk λl λm (n ((k o) m)) (λp λq λr (s ((p t) r)) λ* λu u)))
dup #s m n = k; dup #s r s = p; dup #s b i = (λj λk λl (m ((j n) l)) (λo λp λq (r ((o s) q)) λ* λt t)); dup #s f g = d; λa (b (((λc λd λe (f ((c g) e)) λ* λh h) i) a))
dup #s l s = j; dup #s p q = n; dup #s b i = λj λk (l (((λm λn λo (p ((m q) o)) λ* λr r) s) k)); dup #s f g = d; λa (b (((λc λd λe (f ((c g) e)) λ* λh h) i) a))
dup #s m t = [b j#s]; dup #s q r = o; dup #s c k = λl (m (((λn λo λp (q ((n r) p)) λ* λs s) t) l)); dup #s g h = e; λa (λb c (((λd λe λf (g ((d h) f)) λ* λi i) λj k) a))
dup #s p q = n; dup #s d k = [(((λm λn λo (p ((m q) o)) λ* λr r) λs l) a) s#s]; dup #s h i = f; dup #s b l = λc (d (((λe λf λg (h ((e i) g)) λ* λj j) k) c)); λa b
dup #s q r = o; dup #s d k = [(((λn λo λp (q ((n r) p)) λ* λs s) λt λl m) a) t#s]; dup #s h i = f; dup #s c m = (d (((λe λf λg (h ((e i) g)) λ* λj j) k) [b l#s])); λa λb c
dup #s g h = e; dup #s p q = n; dup #s c l = ((((λd λe λf (g ((d h) f)) λ* λi i) λj λk l) a) (((λm λn λo (p ((m q) o)) λ* λr r) j) [b k#s])); λa λb c
dup #s f h = d; dup #s o p = m; dup #s c k = (((λd λe (f ((λ* λg g h) e)) λi λj k) a) (((λl λm λn (o ((l p) n)) λ* λq q) i) [b j#s])); λa λb c
dup #s e g = λn λo p; dup #s k l = i; dup #s c p = ((λd (e ((λ* λf f g) d)) a) (((λh λi λj (k ((h l) j)) λ* λm m) n) [b o#s])); λa λb c
dup #s d f = λm λn o; dup #s j k = h; dup #s c o = ((d ((λ* λe e f) a)) (((λg λh λi (j ((g k) i)) λ* λl l) m) [b n#s])); λa λb c
dup #s e h = λo p; dup #s l m = j; dup #s c p = ((λd e ((λ* λf f λg h) a)) (((λi λj λk (l ((i m) k)) λ* λn n) [d g#s]) [b o#s])); λa λb c
dup #s d m = λn o; dup #s h i = f; dup #s c o = (d (((λe λf λg (h ((e i) g)) λ* λj j) [((λ* λk k λl m) a) l#s]) [b n#s])); λa λb c
dup #s e o = p; dup #s i j = g; dup #s c p = (λd e (((λf λg λh (i ((f j) h)) λ* λk k) [((λ* λl l λm λn o) a) m#s]) [b [d n#s]#s])); λa λb c
dup #s d f = e; dup #s c e = d; λa λb c

For better readability I also ran it with lazy-REF nodes, then it prints:

(λa (a (S (S Z))) (S (S Z)))
((S (S Z)) (S (S Z)))
dup #s c d = a; (λa λb (c (((S Z) d) b)) (S (S Z)))
dup #s b c = (S (S Z)); λa (b (((S Z) c) a))
dup #s f g = d; dup #s b c = λd λe (f (((S Z) g) e)); λa (b (((S Z) c) a))
dup #s g h = [b d#s]; dup #s c e = λf (g (((S Z) h) f)); λa (λb c (((S Z) λd e) a))
dup #s d e = [(((S Z) λg f) a) g#s]; dup #s b f = λc (d (((S Z) e) c)); λa b
dup #s d e = [(((S Z) λh λf g) a) h#s]; dup #s c g = (d (((S Z) e) [b f#s])); λa λb c
dup #s c f = ((((S Z) λd λe f) a) (((S Z) d) [b e#s])); λa λb c
dup #s f g = d; dup #s c j = (((λd λe (f ((Z g) e)) λh λi j) a) (((S Z) h) [b i#s])); λa λb c
dup #s e f = λg λh i; dup #s c i = ((λd (e ((Z f) d)) a) (((S Z) g) [b h#s])); λa λb c
dup #s d e = λf λg h; dup #s c h = ((d ((Z e) a)) (((S Z) f) [b g#s])); λa λb c
dup #s e g = λh i; dup #s c i = ((λd e ((Z λf g) a)) (((S Z) [d f#s]) [b h#s])); λa λb c
dup #s d f = λg h; dup #s c h = (d (((S Z) [((Z λe f) a) e#s]) [b g#s])); λa λb c
dup #s e h = i; dup #s c i = (λd e (((S Z) [((Z λf λg h) a) f#s]) [b [d g#s]#s])); λa λb c
dup #s d f = e; dup #s c e = d; λa λb c

The dup label #s is reused between S calls but shouldn't be.

Error in rewrite implementation

In net::rewrite, when kinds are the same, x slot 1 is linked to y slot 1 and x slot 2 is linked to y slot 2. Shouldn't this be the other way round for symmetric interaction combinators? So x1 <-> y2 and x2 <-> y1?

Example stackoverflow

I modified the project a little bit to run example.rs with cargo test:

diff --git a/src/example.rs b/src/example.rs
index 85ff15f..bbcaaeb 100644
--- a/src/example.rs
+++ b/src/example.rs
@@ -7,8 +7,7 @@
 // - [-] stands for an erased (unused) lambda or let variable
 // - [/a b c] inlines all occurrences of [a] by the closed term [b]

-mod term;
-mod net;
+use term;

 pub fn example() {
     let example = term::from_string(b"
diff --git a/src/main.rs b/src/main.rs
index d518a19..5387cda 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,6 +4,7 @@ use clap::{Arg, App};
 mod term;
 mod net;
 mod extra;
+mod example;

 use term::*;
 use extra::*;
@@ -104,3 +105,12 @@ fn main() -> io::Result<()> {

     Ok(())
 }
+
+mod tests {
+    use super::example::*;
+
+    #[test]
+    fn test_example() {
+        example();
+    }
+}

After that, I did cargo test and it gives me

thread 'tests::test_example' has overflowed its stack
error: test failed, to rerun pass '--bin sic'

Is example.rs a working example?

benchmarks for comparison

I think it will grate if there is a list of benchmarks to show the advantage compared to existing languages/platforms, such as Solidity/EVM, Haskell, etc. For example, some parallel example that cannot be easily implemented in blockchain because it is hard to eliminate non-deterministic behaviour caused by data race.

The paper https://medium.com/@maiavictor/the-abstract-calculus-fe8c46bcf39c is very good, but simple examples can be more intuitive and be more convincible. Some simple examples will be easier for me to convince my boss. :-)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.