{"id":13608288,"url":"https://github.com/VictorTaelin/Interaction-Calculus","last_synced_at":"2025-04-12T14:32:32.012Z","repository":{"id":44103284,"uuid":"145913607","full_name":"VictorTaelin/Interaction-Calculus","owner":"VictorTaelin","description":"A programming language and model of computation that matches the optimal λ-calculus reduction algorithm perfectly.","archived":false,"fork":false,"pushed_at":"2025-03-27T19:46:14.000Z","size":3791,"stargazers_count":862,"open_issues_count":7,"forks_count":62,"subscribers_count":42,"default_branch":"main","last_synced_at":"2025-04-10T09:07:26.558Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/VictorTaelin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-08-23T22:26:09.000Z","updated_at":"2025-04-07T12:29:55.000Z","dependencies_parsed_at":"2024-01-13T01:47:02.907Z","dependency_job_id":"cc0acf9b-7a64-4c2b-9783-e48e440a05e2","html_url":"https://github.com/VictorTaelin/Interaction-Calculus","commit_stats":null,"previous_names":["victortaelin/symmetric-interaction-calculus","maiavictor/symmetric-interaction-calculus"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VictorTaelin%2FInteraction-Calculus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VictorTaelin%2FInteraction-Calculus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VictorTaelin%2FInteraction-Calculus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VictorTaelin%2FInteraction-Calculus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/VictorTaelin","download_url":"https://codeload.github.com/VictorTaelin/Interaction-Calculus/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248581345,"owners_count":21128151,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T19:01:26.015Z","updated_at":"2025-04-12T14:32:32.005Z","avatar_url":"https://github.com/VictorTaelin.png","language":"C","funding_links":[],"categories":["Programming Language","C"],"sub_categories":["Libraries"],"readme":"# Interaction Calculus\n\nThe Interaction Calculus is a minimal term rewriting system inspired by the\nLambda Calculus (λC), but with some key differences that make it inherently more\nefficient, in a way that closely resembles Lamping's optimal λ-calculus\nevaluator, and more expressive, in some ways. In particular:\n\n1. Vars are affine: they can only occur up to one time.\n\n2. Vars are global: they can occur anywhere in the program.\n\n3. It features first-class *superpositions* and *duplications*.\n\nGlobal lambdas allow the IC to express concepts that aren't possible on the\ntraditional λC, including continuations, linear HOAS, and mutable references.\nSuperpositions and duplications allow the IC to be optimally evaluated, making\nsome computations exponentially faster. Finally, being fully affine makes its\ngarbage collector very efficient, and greatly simplifies parallelism.\n\nThe [HVM](https://github.com/HigherOrderCO/HVM3) is a fast, fully featured\nimplementation of this calculus.\n\n**This repo now includes a reference implementation in C, which is also quite fast!**\n\n**Now it also includes a single-file implementation in Haskell, great for learning!**\n\n## Usage\n\nThis repository includes a reference implementation of the Interaction Calculus\nin plain C, with some additional features, like native numbers. To install it:\n\n```\nmake clean\nmake\n```\n\nThen, run one of the examples:\n\n```\n./bin/ic run examples/test_0.ic\n```\n\nFor learning, edit the Haskell file: it is simpler, and has a step debugger.\n\n## Specification\n\nAn IC term is defined by the following grammar:\n\n```haskell\nTerm ::=\n  | VAR: Name\n  | ERA: \"*\"\n  | LAM: \"λ\" Name \".\" Term\n  | APP: \"(\" Term \" \" Term \")\"\n  | SUP: \"\u0026\" Label \"{\" Term \",\" Term \"}\"\n  | DUP: \"!\" \"\u0026\" Label \"{\" Name \",\" Name \"}\" \"=\" Term \";\" Term\n```\n\nWhere:\n- VAR represents a variable.\n- ERA represents an erasure.\n- LAM represents a lambda.\n- APP represents a application.\n- SUP represents a superposition.\n- DUP represents a duplication.\n\nLambdas are curried, and work like their λC counterpart, except with a relaxed\nscope, and with affine usage. Applications eliminate lambdas, like in λC,\nthrough the beta-reduce (APP-LAM) interaction.\n\nSuperpositions work like pairs. Duplications eliminate superpositions through\nthe DUP-SUP interaction, which works exactly like a pair projection.\n\nWhat makes SUPs and DUPs unique is how they interact with LAMs and APPs. When a\nSUP is applied to an argument, it reduces through the APP-SUP interaction, and\nwhen a LAM is projected, it reduces through the DUP-LAM interaction. This gives\na computational behavior for every possible interaction: there are no runtime\nerrors on the Interaction Calculus.\n\nThe 'Label' is just a numeric value. It affects the DUP-SUP interaction.\n\nThe core interaction rules are listed below:\n\n```haskell\n(* a)\n----- APP-ERA\n*\n\n(λx.f a)\n-------- APP-LAM\nx \u003c- a\nf\n\n(\u0026L{a,b} c)\n----------------- APP-SUP\n! \u0026L{c0,c1} = c;\n\u0026L{(a c0),(b c1)}\n\n! \u0026L{r,s} = *;\nK\n-------------- DUP-ERA\nr \u003c- *\ns \u003c- *\nK\n\n! \u0026L{r,s} = λx.f;\nK\n----------------- DUP-LAM\nr \u003c- λx0.f0\ns \u003c- λx1.f1\nx \u003c- \u0026L{x0,x1}\n! \u0026L{f0,f1} = f;\nK\n\n! \u0026L{x,y} = \u0026L{a,b};\nK\n-------------------- DUP-SUP (if equal labels)\nx \u003c- a\ny \u003c- b\nK\n\n! \u0026L{x,y} = \u0026R{a,b};\nK\n-------------------- DUP-SUP (if different labels)\nx \u003c- \u0026R{a0,b0} \ny \u003c- \u0026R{a1,b1}\n! \u0026L{a0,a1} = a;\n! \u0026L{b0,b1} = b;\nK\n```\n\nWhere `x \u003c- t` stands for a global substitution of `x` by `t`.\n\nSince variables are affine, substitutions can be implemented efficiently by just\ninserting an entry in a global substitution map (`sub[var] = value`). There is\nno need to traverse the target term, or to handle name capture, as long as fresh\nvariable names are globally unique. It can also be implemented in a concurrent\nsetup with a single atomic-swap.\n\nBelow is a pseudocode implementation of these interaction rules:\n\n```python\ndef app_lam(app, lam):\n  sub[lam.nam] = app.arg\n  return lam.bod\n\ndef app_sup(app, sup):\n  x0 = fresh()\n  x1 = fresh()\n  a0 = App(sup.lft, Var(x0))\n  a1 = App(sup.rgt, Var(x1))\n  return Dup(sup.lab, x0, x1, app.arg, Sup(a0, a1))\n\ndef dup_lam(dup, lam):\n  x0 = fresh()\n  x1 = fresh()\n  f0 = fresh()\n  f1 = fresh()\n  sub[dup.lft] = Lam(x0, Var(f0))\n  sub[dup.rgt] = Lam(x1, Var(f1))\n  sub[lam.nam] = Sup(dup.lab, Var(x0), Var(x1))\n  return Dup(dup.lab, f0, f1, lam.bod, dup.bod)\n\ndef dup_sup(dup, sup):\n  if dup.lab == sup.lab:\n    sub[dup.lft] = sup.lft\n    sub[dup.rgt] = sup.rgt\n    return dup.bod\n  else:\n    a0 = fresh()\n    a1 = fresh()\n    b0 = fresh()\n    b1 = fresh()\n    sub[dup.lft] = Sup(sup.lab, Var(a0), Var(b0))\n    sub[dup.rgt] = Sup(sup.lab, Var(a1), Var(b1))\n    return Dup(dup.lab, a0, a1, sup.lft, Dup(dup.lab, b0, b1, sup.rgt, dup.bod))\n```\n\nTerms can be reduced to weak head normal form, which means reducing until the\noutermost constructor is a value (LAM, SUP, etc.), or until no more reductions\nare possible. Example:\n\n```python\ndef whnf(term):\n  while True:\n    match term:\n      case Var(nam):\n        if nam in sub:\n          term = sub[nam]\n        else:\n          return term\n      case App(fun, arg):\n        fun = whnf(fun)\n        match fun.tag:\n          case Lam: term = app_lam(term, fun)\n          case Sup: term = app_sup(term, fun)\n          case _  : return App(fun, arg)\n      case Dup(lft, rgt, val, bod):\n        val = whnf(val)\n        match val.tag:\n          case Lam: term = dup_lam(term, val)\n          case Sup: term = dup_sup(term, val)\n          case _  : return Dup(lft, rgt, val, bod)\n      case _:\n        return term\n```\n\nTerms can be reduced to full normal form by recursively taking the whnf:\n\n```python\ndef normal(term):\n  term = whnf(term)\n  match term:\n    case Lam(nam, bod):\n      bod_nf = normal(bod)\n      return Lam(nam, bod_nf)\n    case App(fun, arg):\n      fun_nf = normal(fun)\n      arg_nf = normal(arg)\n      return App(fun_nf, arg_nf)\n    ...\n    case _:\n      return term\n```\n\nBelow are some normalization examples.\n\nExample 0: (simple λ-term)\n\n```\n(λx.λt.(t x) λy.y)\n------------------ APP-LAM\nλt.(t λy.y)\n```\n\nExample 1: (larger λ-term)\n\n```\n(λb.λt.λf.((b f) t) λT.λF.T)\n---------------------------- APP-LAM\nλt.λf.((λT.λF.T f) t)\n----------------------- APP-LAM\nλt.λf.(λF.t f)\n-------------- APP-LAM\nλt.λf.t\n```\n\nExample 2: (global scopes)\n\n```\n{x,(λx.λy.y λk.k)}\n------------------ APP-LAM\n{λk.k,λy.y}\n```\n\nExample 3: (superposition)\n\n```\n!{a,b} = {λx.x,λy.y}; (a b)\n--------------------------- DUP-SUP\n(λx.x λy.y)\n----------- APP-LAM\nλy.y\n```\n\nExample 4: (overlap)\n\n```\n({λx.x,λy.y} λz.z)\n------------------ APP-SUP  \n! {x0,x1} = λz.z; {(λx.x x0),(λy.y x1)}  \n--------------------------------------- DUP-LAM  \n! {f0,f1} = {r,s}; {(λx.x λr.f0),(λy.y λs.f1)}  \n---------------------------------------------- DUP-SUP  \n{(λx.x λr.r),(λy.y λs.s)}  \n------------------------- APP-LAM  \n{λr.r,(λy.y λs.s)}  \n------------------ APP-LAM  \n{λr.r,λs.s}  \n```\n\nExample 5: (default test term)\n\nThe following term can be used to test all interactions:\n\n```\n((λf.λx.!{f0,f1}=f;(f0 (f1 x)) λB.λT.λF.((B F) T)) λa.λb.a)\n----------------------------------------------------------- 16 interactions\nλa.λb.a\n```\n\n## Collapsing\n\nAn Interaction Calculus term can be collapsed to a superposed tree of pure\nLambda Calculus terms without SUPs and DUPs, by extending the evaluator with the\nfollowing collapse interactions:\n\n```haskell\nλx.*\n------ ERA-LAM\nx \u003c- *\n*\n\n(f *)\n----- ERA-APP\n*\n\nλx.\u0026L{f0,f1}\n----------------- SUP-LAM\nx \u003c- \u0026L{x0,x1}\n\u0026L{λx0.f0,λx1.f1}\n\n(f \u0026L{x0,x1})\n------------------- SUP-APP\n!\u0026L{f0,f1} = f;\n\u0026L{(f0 x0),(f1 x1)}\n\n!\u0026L{x0,x1} = x; K\n----------------- DUP-VAR\nx0 \u003c- x\nx1 \u003c- x\nK\n\n!\u0026L{a0,a1} = (f x); K\n--------------------- DUP-APP\na0 \u003c- (f0 x0)\na1 \u003c- (f1 x1)\n!\u0026L{f0,f1} = f;\n!\u0026L{x0,x1} = x;\nK\n```\n\n## DUP Permutations\n\nThese interactions move a nested DUP out of a redex position.\n\n```\n(!\u0026L{k0,k1}=k;f x)\n------------------ APP-DUP\n!\u0026L{k0,k1}=k;(f x)\n\n! \u0026L{x0,x1} = (!$R{y0,y1}=Y;X); T\n------------------------------------- DUP-DUP\n! \u0026L{x0,x1} = X; ! \u0026L{y0,y1} = Y; T\n```\n\nThey're only needed in implementations that store a DUP's body.\n\n## Labeled Lambdas\n\nAnother possible extension of IC is to include labels on lams/apps:\n\n```haskell\n  | LAM: \"\u0026\" Label \"λ\" Name \".\" Term\n  | APP: \"\u0026\" Label \"(\" Term \" \" Term \")\"\n```\n\nThe APP-LAM rule must, then, be extended with:\n\n```haskell\n\u0026L(\u0026Rλx.bod arg)\n----------------------- APP-LAM (if different labels)\nx \u003c- \u0026Lλy.z\n\u0026Rλz.\u0026L(body \u0026R(arg y))\n```\n\n## IC = Lambda Calculus U Interaction Combinators\n\nConsider the conventional Lambda Calculus, with pairs. It has two computational rules:\n\n- Lambda Application : `(λx.body arg)`\n\n- Pair Projection : `let {a,b} = {fst,snd} in cont`\n\nWhen compiling the Lambda Calculus to Interaction Combinators:\n\n- `lams` and `apps` can be represented as constructor nodes (γ) \n\n- `pars` and `lets` can be represented as duplicator nodes (δ)\n\nAs such, lambda applications and pair projections are just annihilations:\n\n```\n      Lambda Application                 Pair Projection\n                                                                   \n      (λx.body arg)                      let {a,b} = {fst,snd} in cont \n      ----------------                   -----------------------------\n      x \u003c- arg                           a \u003c- fst                  \n      body                               b \u003c- snd                  \n                                         cont                      \n                                                                   \n    ret  arg    ret  arg                  b   a       b    a       \n     |   |       |    |                   |   |       |    |       \n     |___|       |    |                   |___|       |    |       \n app  \\ /         \\  /                let  \\#/         \\  /        \n       |    ==\u003e    \\/                       |    ==\u003e    \\/         \n       |           /\\                       |           /\\         \n lam  /_\\         /  \\               pair  /#\\         /  \\        \n     |   |       |    |                   |   |       |    |       \n     |   |       |    |                   |   |       |    |       \n     x  body     x   body                fst snd    fst   snd      \n                                                                   \n \"The application of a lambda        \"The projection of a pair just \n substitutes the lambda's var        substitutes the projected vars\n by the application's arg, and       by each element of the pair, and\n returns the lambda body.\"           returns the continuation.\"\n```\n\nBut annihilations only happen when identical nodes interact. On interaction\nnets, it is possible for different nodes to interact, which triggers another rule,\nthe commutation. That rule could be seen as handling the following expressions:\n\n- Lambda Projection : `let {a b} = (λx body) in cont`\n\n- Pair Application : `({fst snd} arg)`\n\nBut how could we \"project\" a lambda or \"apply\" a pair? On the Lambda Calculus, these\ncases are undefined and stuck, and should be type errors. Yet, by interpreting the\neffects of the commutation rule on the interaction combinator point of view, we\ncan propose a reasonable reduction for these lambda expressions:\n\n```\n   Lambda Application                         Pair Application\n                                                                  \n   let {a,b} = (λx.body) in cont             ({fst,snd} arg)   \n   ------------------------------             ---------------\n   a \u003c- λx0.b0                               let {x0,x1} = arg in\n   b \u003c- λx1.b1                               {(fst x0),(snd x1)}\n   x \u003c- {x0,x1}\n   let {b0,b1} = body in\n   cont                   \n       \n    ret  arg         ret  arg            ret  arg         ret  arg  \n     |   |            |    |              |   |            |    |   \n     |___|            |    |              |___|            |    |   \n let  \\#/            /_\\  /_\\         app  \\ /            /#\\  /#\\  \n       |      ==\u003e    |  \\/  |               |      ==\u003e    |  \\/  |  \n       |             |_ /\\ _|               |             |_ /\\ _|  \n lam  /_\\            \\#/  \\#/        pair  /#\\            \\ /  \\ /  \n     |   |            |    |              |   |            |    |   \n     |   |            |    |              |   |            |    |   \n     x  body          x   body           var body         var  body \n\n \"The projection of a lambda         \"The application of a pair is a pair\n substitutes the projected vars      of the first element and the second\n by a copies of the lambda that      element applied to projections of the\n return its projected body, with     application argument.\"\n the bound variable substituted\n by the new lambda vars paired.\"\n```\n\nThis, in a way, completes the lambda calculus; i.e., previously \"stuck\"\nexpressions now have a meaningful computation. That system, as written, is\nTuring complete, yet, it is very limited, since it isn't capable of cloning\npairs, or cloning cloned lambdas. There is a simple way to greatly increase its\nexpressivity, though: by decorating lets with labels, and upgrading the pair\nprojection rule to:\n\n```haskell\nlet \u0026i{a,b} = \u0026j{fst,snd} in cont\n---------------------------------\nif i == j:\n  a \u003c- fst\n  b \u003c- snd\n  cont\nelse:\n  a \u003c- \u0026j{a0,a1}\n  b \u003c- \u0026j{b0,b1} \n  let \u0026i{a0,a1} = fst in\n  let \u0026i{b0,b1} = snd in\n  cont\n```\n\nThat is, it may correspond to either an Interaction Combinator annihilation or\ncommutation, depending on the value of the labels `\u0026i` and `\u0026j`. This makes IC\ncapable of cloning pairs, cloning cloned lambdas, computing nested loops,\nperforming Church-encoded arithmetic up to exponentiation, expressing arbitrary\nrecursive functions such as the Y-combinators and so on. In other words, with\nthis simple extension, IC becomes extraordinarily powerful and expressive,\ngiving us a new foundation for symbolic computing, that is, in many ways, very\nsimilar to the λ-Calculus, yet, with key differences that make it more\nefficient in some senses, and capable of expressing new things (like call/cc,\nO(1) queues, linear HOAS), but unable to express others (like `λx.(x x)`).\n\n## IC32: a 32-Bit Runtime\n\nIC32 is implemented in portable C.\n\nEach Term is represented as a 32-bit word, split into the following fields:\n\n- sub (1-bit): true if this is a substitution\n- tag (5-bit): the tag identifying the term type and label\n- val (26-bit): the value, typically a pointer to a node in memory\n\nThe tag field can be one of the following:\n\n- `VAR`: 0x00\n- `LAM`: 0x01\n- `APP`: 0x02\n- `ERA`: 0x03\n- `NUM`: 0x04\n- `SUC`: 0x05\n- `SWI`: 0x06\n- `TMP`: 0x07\n- `SP0`: 0x08\n- `SP1`: 0x09\n- `SP2`: 0x0A\n- `SP3`: 0x0B\n- `SP4`: 0x0C\n- `SP5`: 0x0D\n- `SP6`: 0x0E\n- `SP7`: 0x0F\n- `CX0`: 0x10\n- `CX1`: 0x11\n- `CX2`: 0x12\n- `CX3`: 0x13\n- `CX4`: 0x14\n- `CX5`: 0x15\n- `CX6`: 0x16\n- `CX7`: 0x17\n- `CY0`: 0x18\n- `CY1`: 0x19\n- `CY2`: 0x1A\n- `CY3`: 0x1B\n- `CY4`: 0x1C\n- `CY5`: 0x1D\n- `CY6`: 0x1E\n- `CY7`: 0x1F\n\nThe val field depends on the variant:\n\n- `VAR`: points to a Lam node ({bod: Term}) or a substitution.\n- `LAM`: points to a Lam node ({bod: Term}).\n- `APP`: points to an App node ({fun: Term, arg: Term}).\n- `ERA`: unused.\n- `NUM`: stores an unsigned integer.\n- `SUC`: points to a Suc node ({num: Term})\n- `SWI`: points to a Swi node ({num: Term, ifZ: Term, ifS: Term})\n- `SP{L}`: points to a Sup node ({lft: Term, rgt: Term}).\n- `CX{L}`: points to a Dup node ({val: Term}) or a substitution.\n- `CY{L}`: points to a Dup node ({val: Term}) or a substitution.\n\nA node is a consecutive block of its child terms. For example, the SUP term\npoints to the memory location where its two child terms are stored.\n\nVariable terms (`VAR`, `CX{L}`, and `CY{L}`) point to the location where the\nsubstitution will be placed. As an optimization, that location is always the\nlocation of the corresponding binder node (like a Lam or Dup). When the\ninteraction occurs, we replace the binder node by the substituted term, with the\n'sub' bit set. Then, when we access it from a variable, we retrieve that term,\nclearing the bit.\n\nOn SUPs and DUPs, the 'L' stands for the label of the corresponding node.\n\nNote that there is no explicit DUP term. That's because Dup nodes are special:\nthey aren't part of the AST, and they don't store a body; they \"float\" on the\nheap.  In other words, `λx. !\u00260{x0,x1}=x; \u00260{x0,x1}` and `!\u00260{x0,x1}=x; λx.\n\u00260{x0,x1}` are both valid, and stored identically in memory. As such, the only\nway to access a Dup node is via its bound variables, `CX{L}` and `CY{L}`.\n\nBefore the interaction, the Dup node stores just the duplicated value (no body).\nAfter a collapse is triggered (when we access it via a `CX{L}` or `CY{L}`\nvariable), the first half of the duplicated term is returned, and the other half\nis stored where the Dup node was, allowing the other variable to get it as a\nsubstitution. For example, the DUP-SUP interaction could be implemented as:\n\n```python\ndef dup_sup(dup, sup):\n  dup_lab = dup.tag \u0026 0x3\n  sup_lab = sup.tag \u0026 0x3\n  if dup_lab == sup_lab:\n    tm0 = heap[sup.loc + 0]\n    tm1 = heap[sup.loc + 1]\n    heap[dup.loc] = as_sub(tm1 if (dup.tag \u0026 0x4) == 0 else tm0)\n    return (tm0 if (dup.tag \u0026 0x4) == 0 else tm1)\n  else:\n    co0_loc = alloc(1)\n    co1_loc = alloc(1)\n    su0_loc = alloc(2)\n    su1_loc = alloc(2)\n    su0_val = Term(SP0 + sup_lab, su0_loc)\n    su1_val = Term(SP0 + sup_lab, su1_loc)\n    heap[co0_loc] = heap[sup.loc + 0]\n    heap[co1_loc] = heap[sup.loc + 1]\n    heap[su0_loc + 0] = Term(CX0 + dup_lab, co0_loc)\n    heap[su0_loc + 1] = Term(CX0 + dup_lab, co1_loc)\n    heap[su1_loc + 0] = Term(CY0 + dup_lab, co0_loc)\n    heap[su1_loc + 1] = Term(CY0 + dup_lab, co1_loc)\n    heap[dup.loc] = as_sub(su1_val if (dup.tag \u0026 0x4) == 0 else su0_val)\n    return (su0_val if (dup.tag \u0026 0x4) == 0 else su1_val)\n```\n\nThe NUM, SUC and SWI terms extend the IC with unboxed unsigned integers.\n\n## Parsing IC32\n\nOn IC32, all bound variables have global range. For example, consider the term:\n\nλt.((t x) λx.λy.y)\n\nHere, the `x` variable appears before its binder, `λx`. Since runtime variables\nmust point to their bound λ's, linking them correctly requires caution. A way to\ndo it is to store two structures at parse-time:\n\n1. lcs: an array from names to locations\n2. vrs: a map from names to var terms\n\nWhenever we parse a name, we add the current location to the 'uses' array, and\nwhenever we parse a binder (lams, lets, etc.), we add a variable term pointing\nto it to the 'vars' map. Then, once the parsing is done, we run iterate through\nthe 'uses' array, and write, to each location, the corresponding term. Below\nare some example parsers using this strategy:\n\n```python\ndef parse_var(loc):\n  nam = parse_name()\n  uses.push((nam,loc))\n\ndef parse_lam(loc):\n  lam = alloc(1)\n  consume(\"λ\")\n  nam = parse_name()\n  consume(\".\")\n  vars[nam] = Term(VAR, 0, lam)\n  parse_term(lam)\n  heap[loc] = Term(LAM, 0, lam)\n\ndef parse_app(loc):\n  app = alloc(2)\n  consume(\"(\")\n  parse_term(app + 0)\n  consume(\" \")\n  parse_term(app + 1)\n  consume(\")\")\n  heap[loc] = Term(APP, 0, app)\n\ndef parse_sup(loc):\n  sup = alloc(2)\n  consume(\"\u0026\")\n  lab = parse_uint()\n  consume(\"{\")\n  lft = parse_term(sup + 0)\n  consume(\",\")\n  rgt = parse_term(sup + 1)\n  consume(\"}\")\n  heap[loc] = Term(SUP, lab, sup)\n\ndef parse_dup(loc):\n  dup = alloc(1)\n  consume(\"!\")\n  consume(\"\u0026\")\n  lab = parse_uint()\n  consume(\"{\")\n  co0 = parse_name()\n  consume(\",\")\n  co1 = parse_name()\n  consume(\"}\")\n  consume(\"=\")\n  val = parse_term(dup)\n  bod = parse_term(loc)\n  vars[co0] = Term(DP0, lab, loc)\n  vars[co1] = Term(DP1, lab, loc)\n```\n\n## Stringifying IC32\n\nConverting IC32 terms to strings faces two challenges:\n\nFirst, IC32 terms and nodes don't store variable names. As such, we must\ngenerate fresh, unique variable names during stringification, and maintain a\nmapping from each binder's memory location to its assigned name.\n\nSecond, on IC32, Dup nodes aren't part of the main program's AST. Instead,\nthey \"float\" on the heap, and are only reachable via DP0 and DP1 variables.\nBecause of that, by stringifying a term naively, Col nodes will be missing.\n\nTo solve these, we proceed as follows:\n\n1. Before stringifying, we pass through the full term, and assign a id to each\nvariable binder we find (on lam, let, dup, etc.)\n\n2. We also register every Dup node we found, avoiding duplicates (remember the\nsame dup node is pointed to by up to 2 variables, DP0 and DP1)\n\nThen, to stringify the term, we first stringify each DUP node, and then we\nstringify the actual term. As such, the result will always be in the form:\n\n```haskell\n! \u0026{x0 x1} = t0\n! \u0026{x2 x3} = t1\n! \u0026{x4 x5} = t2\n...\nterm\n```\n\nWith no Dup nodes inside the ASTs of t0, t1, t2 ... and term.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FVictorTaelin%2FInteraction-Calculus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FVictorTaelin%2FInteraction-Calculus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FVictorTaelin%2FInteraction-Calculus/lists"}