{"id":42180245,"url":"https://github.com/inazak/lambda-calculus-machine","last_synced_at":"2026-01-26T22:03:32.775Z","repository":{"id":190754785,"uuid":"683004368","full_name":"inazak/lambda-calculus-machine","owner":"inazak","description":"stack-based machine","archived":false,"fork":false,"pushed_at":"2023-12-14T22:50:23.000Z","size":23,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-06-21T04:53:03.154Z","etag":null,"topics":["golang","lambda-calculus","stack-based"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/inazak.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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-08-25T11:19:08.000Z","updated_at":"2023-08-29T22:54:11.000Z","dependencies_parsed_at":"2023-12-14T23:44:12.017Z","dependency_job_id":null,"html_url":"https://github.com/inazak/lambda-calculus-machine","commit_stats":null,"previous_names":["inazak/lambda-calculus-machine"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/inazak/lambda-calculus-machine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inazak%2Flambda-calculus-machine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inazak%2Flambda-calculus-machine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inazak%2Flambda-calculus-machine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inazak%2Flambda-calculus-machine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/inazak","download_url":"https://codeload.github.com/inazak/lambda-calculus-machine/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inazak%2Flambda-calculus-machine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28789721,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-26T21:49:50.245Z","status":"ssl_error","status_checked_at":"2026-01-26T21:48:29.455Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["golang","lambda-calculus","stack-based"],"created_at":"2026-01-26T22:02:23.356Z","updated_at":"2026-01-26T22:03:32.765Z","avatar_url":"https://github.com/inazak.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lambda-calculus-machine\n\nSECDマシンよりもっと単純な、型無しラムダ計算のスタックマシンを作る。\n\n\n## 実行例\n\nexampleで `100 mod 13` を実行している。\n\n```\ninput:\n(((^f.(^x.(f (x x)) ^x.(f (x x))) ^f.^m.^n.(((^b.b ((^x.^y.(^x.((x (^x.^y.x ^x.^\ny.y)) ^x.^y.x) ((^x.^y.((y ^n.^f.^x.(((n ^g.^h.(h (g f))) ^u.x) ^u.u)) x) x) y))\n n) m)) ((f ((^x.^y.((y ^n.^f.^x.(((n ^g.^h.(h (g f))) ^u.x) ^u.u)) x) m) n)) n)\n) m)) ^f.^x.(f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f\n (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (\nf (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f \n(f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f x)))))))\n))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))\n)))))))))))))) ^f.^x.(f (f (f (f (f (f (f (f (f (f (f (f (f x))))))))))))))\n```\n\n結果は `9` となる。\n\n```\noutput:\n^f.^x.(f (f (f (f (f (f (f (f (f x)))))))))\n```\n\n## スタックマシンの構造\n\nまずラムダ式を表現するExpressionを定義する。\n\n- Symbol\n- Function\n- Application\n\nこのExpressionで表現されたASTをコンパイルして、\nVMで実行するInstructionに変換する。\n\n- Fetch\n- Call\n- Close\n- Apply\n- Return\n\nVMにはEnvironmentというmapと、スタックがある。\n\n```\ntype VM struct {\n\tstack []Value\n\tenv   Environment //type Environment map[string]Value\n\tcode  []Instruction\n}\n```\n\nValueはInstructionを実行した結果としてスタックに生成される下記の値。\n最終的には `Symbol` `Application` `Function` のいずれかがスタック上に残り、\n結果を返すようにしている。\n\n- Symbol\n- Application\n- Function\n- Callure\n- Closure\n- Dump\n\nASTがInstructionに変換され、Instructionが実行された場合にどのValueになるか、は\n表の通り。\n\n| AST          | Instruction         | Stack Value          |\n| ----         | ----                | ----                 |\n| Symbol       | Fetch               | Symbol               |\n| Application  | Call, Apply, Return | Callure {Env, Code}  |           \n| Function     | Close, Return       | Closure {Env, Code}  |\n\nInstructionのうち `Abstract` がコンパイル時に生成されない。\nこれはラムダ抽象の内部を簡約する際に、使われている。\n簡約を試みるラムダ抽象の引数を、Symbolとしてスタックにプッシュし、\n同時に `Abstract` をInstructionの先頭に差し込む。\n簡約が終わって復帰したら、`Abstract` が呼ばれて、\nスタックに積んである簡約結果と、Symbolを使って `Function` を生成する。\n \n\n## Case.1\n\nラムダ式が単純にシンボル一つの場合。\n\n```\nx\n```\n\nASTは次の通り。\n\n```\nSymbol { Name: \"x\" }\n```\n\nコンパイルすると、Fetchだけになる。\n\n```\nFetch x\n```\n\nこのコードを実行する直前の状態は次の通り。\n\n```\ncode:\n  Fetch x\n\nenv:\n  (empty)\n\nstack:\n  (empty)\n```\n\n先頭のFetchが実行されると、xをenvから探す。\nしかし見つからないので、スタックにSymbolがPushされる。\n\n```\ncode:\n  (empty)\n\nenv:\n  (empty)\n\nstack:\n  +---------------------------+\n  |        Symbol(x)          |\n  +---------------------------+\n```\n\ncodeが空になったので、スタックの先頭であるSymbolを取り出し、結果として返す。\n\n```\nx\n```\n\n\n## Case.2\n\nラムダ式が単純な適用の場合。\n\n```\n(x y)\n```\n\nASTは次の通り。\n\n```\nApplication { Left:  Symbol { Name: \"x\" },\n              Right: Symbol { Name: \"y\" }, }\n```\n\nコンパイルすると、Callになる。\n```\nCall [ Fetch x; Fetch y; Apply; Return ]\n```\n\nこのコードを実行する直前の状態は次の通り。\n\n```\ncode:\n  Call [ Fetch x; Fetch y; Apply; Return ]\n\nenv:\n  (empty)\n\nstack:\n  (empty)\n```\n\nCallが実行されると、現在のenvをキャプチャした\nCallureがスタックにPushされる。\n\n```\ncode:\n  (empty)\n\nenv:\n  (empty)\n\nstack:\n  +---------------------------+\n  |    Callure [env,code]     |\n  +---------------------------+\n```\n\ncodeが空になって、かつスタックの先頭がCallureなので\nスタックからCallureを取り出して、次の操作を行う。\n\n1. 現在のenvとcodeの内容を、Dumpに詰め込んで、スタックにPushする\n2. envとcodeをCallureの保持している内容で上書きする\n\n\n次のようになる。\n\n```\ncode:\n  Fetch x   \u003c-- Callure.Code で上書きした\n  Fetch y   \u003c-- Callure.Code で上書きした\n  Apply     \u003c-- Callure.Code で上書きした\n  Return    \u003c-- Callure.Code で上書きした\n\nenv:\n  (empty)   \u003c-- Callure.Env で上書き\n\nstack:\n  +------------------------------+\n  |      Dump(code,env)          |\n  +------------------------------+\n```\n\n二つのFetchが実行されると、スタックにSymbolがPushされた状態になる。\n\n```\ncode:\n  Apply\n  Return\n\nenv:\n  (empty)\n\nstack:\n  +------------------------------+\n  |          Symbol(y)           |\n  +------------------------------+\n  |          Symbol(x)           |\n  +------------------------------+\n```\n\nApplyはスタックから二つPopする。二つ目（関数側）がClosureではないので、\n単純にApplicationとして再度Pushする。\n\n```\ncode:\n  Return\n\nenv:\n  (empty)\n\nstack:\n  +------------------------------+\n  |     Application(x y)         |\n  +------------------------------+\n  |      Dump(code,env)          |\n  +------------------------------+\n```\n\n次にReturnが実行される。スタックから二つPopする。\n一つ目を処理結果として返すために、もう一度スタックにPushする。\n二つ目にPopした値はDumpなので、このDumpに保持している値で\nenvとcodeを上書きする、つまり以前の状態に戻す。\n\nReturnを実行した後が次の通り。\n\n```\ncode:\n  (empty)   \u003c-- Dump.Code で上書きした\n\nenv:\n  (empty)   \u003c-- Dump.Env で上書きした\n\nstack:\n  +------------------------------+\n  |     Application(x y)         |\n  +------------------------------+\n```\n\ncodeが空になったので、スタックの先頭である\nApplicationを取り出し、結果として返す。\n\n\n## Case.3\n\nラムダ抽象の場合。\n\n```\n^x.x\n```\n\nASTは次の通り。\n\n```\nFunction { Arg:  \"x\", Body: Symbol { Name: \"x\" } }\n```\n\nコンパイルすると、Closeになる。\nパラメータとして、ラムダ変数の文字と、本体部分をコンパイルした値を持つ。\nさらに、本体をコンパイルしたコード部分には、末尾に Return を付加する。\n今回の場合、次の通り。\n\n```\nClose x, [Fetch x; Return]\n```\n\nコンパイルされたCloseが実行されると、そのラムダ変数と本体、\n実行時のenvをまとめたClosureを作って、スタックにPushする。\n\nこのコードを実行する直前の状態は次の通り。\n\n```\ncode:\n  Close x, [Fetch x; Return]\n\nenv:\n  (empty)\n\nstack:\n  (empty)\n```\n\nCloseが実行されると、スタックにClosureがPushされた状態になる。\n\n```\ncode:\n  (empty)\n\nenv:\n  (empty)\n\nstack:\n  +------------------------------+\n  | Closure x, [Fetch x; Return] |\n  +------------------------------+\n```\n\ncodeが空になったので、スタックの先頭であるClosureを取り出し、結果として返す。\n\n```\ncode:\n  (empty)\n\nenv:\n  (empty)\n\nstack:\n  (empty)\n\nresult:\n  Closure x, [Fetch x; Return]\n```\n\nしかしClosureはまだ内部に簡約できる可能性が残っている。そのため次の操作を行う。\n\n1. ClosureのArgを、スタックにSymbolとしてPushする\n2. codeにAbstractを挿入する\n3. この状態でcodeとenvの内容を、Dumpに詰め込んで、スタックにPushする\n4. envとcodeを、Closureの保持している内容で上書きする\n5. ClosureのArgを、envから削除する\n\nまず 1. と 2. を実行すると次のようになる。\n\n```\ncode:\n  Abstract\n\nenv:\n  (empty)\n\nstack:\n  +------------------------------+\n  |         Symbol(x)            |\n  +------------------------------+\n\nresult:\n  Closure x, [Fetch x; Return]\n```\n\n次に 3. のDumpを実行する。\n\n```\ncode:\n  (empty)\n\nenv:\n  (empty)\n\nstack:\n  +----------------------------------+\n  | Dump(env=empty, code=[Abstract]) |\n  +----------------------------------+\n  |         Symbol(x)                |\n  +----------------------------------+\n\nresult:\n  Closure x, [Fetch x; Return]\n```\n\nそして 4. でenvとcodeを上書きする。\n\n```\ncode:\n  Fetch x    \u003c-- Closure.Code で上書きした\n  Return     \u003c-- Closure.Code で上書きした\n\nenv:\n  ?=xxxx     \u003c-- Closure.Code で上書きした\n\nstack:\n  +----------------------------------+\n  | Dump(env=empty, code=[Abstract]) |\n  +----------------------------------+\n  |         Symbol(x)                |\n  +----------------------------------+\n\nresult:\n  Closure x, [Fetch x; Return]\n```\n\n最後に 5. でClosureのArgと同じ名前のキーがあれば削除する。\nなぜなら、この操作ではラムダ変数を無視して本体を評価しているため、\nラムダ変数が何かに展開されることはあってはならない。\n言い換えると、ラムダ変数は束縛されていない状態で、本体を評価する。\nこの状態で実行を続ける。\n\n```\ncode:\n  Fetch x\n  Return\n\nenv:\n  (empty)\n\nstack:\n  +----------------------------------+\n  | Dump(env=empty, code=[Abstract]) |\n  +----------------------------------+\n  |         Symbol(x)                |\n  +----------------------------------+\n```\n\n最初のFetchはxをenvから探すが、これは先の 5. で削除されているので見つからない。\nSymbolがPushされて、Return待ちとなる。\n\n```\ncode:\n  Return\n\nenv:\n  ?=xxxx\n\nstack:\n  +----------------------------------+\n  |         Symbol(x)                |\n  +----------------------------------+\n  | Dump(env=empty, code=[Abstract]) |\n  +----------------------------------+\n  |         Symbol(x)                |\n  +----------------------------------+\n```\n\n結果としてSymbol(x)がスタックトップに残り、codeとenvはDumpで書き戻しされる。\n\n```\ncode:\n  Abstract      \u003c-- Dump.Code で上書きした\n\nenv:\n  (empty)   \u003c-- Dump.Env で上書きした\n\nstack:\n  +------------------------------+\n  |         Symbol(x)            |\n  +------------------------------+\n  |         Symbol(x)            |\n  +------------------------------+\n```\n\ncodeにあるAbstractはスタックの先頭を本体とし、\nスタックの次のSymbolをラムダ変数とした\nFunctionを生成して、スタックにPushする。\n\n```\ncode:\n  (empty)\n\nenv:\n  (empty)\n\nstack:\n  +-----------------------------------+\n  | Function{Arg=\"x\", Body=Symbol(x)} |\n  +-----------------------------------+\n```\n\n最終的にFunctionの文字列表現として下記となる。\n\n```\n^x.x\n```\n\n\n## Case.4\n\nラムダ式が関数への適用の場合。\n\n```\n(^x.^y.x (x z))\n```\n\nASTは次の通り。\n\n```\nApplication { Left: Function { Arg:  \"x\", Body: \n                      Function { Arg:  \"y\", Body: \n                        Symbol { Name: \"x\" } } },\n              Right: Application { Left: Symbol { Name: \"x\" },\n                                   Right: Symbol { Name: \"z\" } } }\n```\n\nコンパイルすると、Call になる。\n\n```\nCall [ Close x, [ Close y, [ Fetch x ; Return] ; Return ] ;\n       Call [ Fetch x ; Fetch z ; Apply ; Return ] ;\n       Apply ;\n       Return ]\n```\n\nまずCallが実行されてCallureがスタックにPushされる。\ncodeが空になるので、Callureの保持しているEnvとCodeが展開される。\nここまでは Case.2 と同じ。\n\n```\ncode:\n  Close x, [ Close y [ Fetch x ; Return ] ; Return ]   \u003c-- Callure.Code で上書きした\n  Call [ Fetch x ; Fetch z ; Apply ; Return ]          \u003c-- Callure.Code で上書きした\n  Apply                                                \u003c-- Callure.Code で上書きした\n  Return                                               \u003c-- Callure.Code で上書きした\n\nenv:\n  (empty)    \u003c-- Callure.Env で上書きした\n\nstack:\n  +---------------------------------+\n  | Dump env=(empty), code=(empty ) |\n  +---------------------------------+\n```\n\n順次codeを実行していくと、Applyになる。\nCallureは先に評価（展開）されることはなく、Callureという値のまま、関数に適用される。\n\n```\ncode:\n  Apply\n  Return\n\nenv:\n  (empty)\n\nstack:\n  +---------------------------------------------------------------------+\n  | Callure [ Fetch x ; Fetch z ; Apply ; Return ]                      |\n  +---------------------------------------------------------------------+\n  | Closure x, env=(empty), code=[Close y, [Fetch x ; Return] ; Return] |\n  +---------------------------------------------------------------------+\n  | Dump env=(empty), code=(empty)                                      |\n  +---------------------------------------------------------------------+\n```\n\ncodeとenvはClosureの保持している値で上書きされる。\nenvはemptyで上書きされるが、Closureのラムダ変数 x に Callure が束縛されるので、\nこの組み合わせが保持される。\n\n\n```\ncode:\n  Close y, [Fetch x ; Return]\n  Return\n\nenv:\n  x = Callure [ Fetch x ; Fetch z ; Apply ; Return ]\n\nstack:\n  +---------------------------------------------------------------------+\n  | Dump env=(empty), code= [Return]                                    |\n  +---------------------------------------------------------------------+\n  | Dump env=(empty), code=(empty)                                      |\n  +---------------------------------------------------------------------+\n```\n\nCloseがClosureをPushして、Returnになる。\n\n```\ncode:\n  Return\n\nenv:\n  x = Callure [ Fetch x ; Fetch z ; Apply ; Return ]\n\nstack:\n  +---------------------------------------------------------------------+\n  | Closure y, env=x:\u003ccallure\u003e, code=[Fetch x ; Return]                 |\n  +---------------------------------------------------------------------+\n  | Dump env=(empty), code= [Return]                                    |\n  +---------------------------------------------------------------------+\n  | Dump env=(empty), code=(empty)                                      |\n  +---------------------------------------------------------------------+\n```\n\nスタックからClosureとDumpをPopして、Dumpのenvとcodeを展開する。\nスタックにはClosureを戻す。\n\n```\ncode:\n  Return   \u003c-- Dump から上書き\n\nenv:\n  (empty)  \u003c-- Dump から上書き\n\nstack:\n  +---------------------------------------------------------------------+\n  | Closure y, env=x:\u003ccallure\u003e, code=[Fetch x ; Return]                 |\n  +---------------------------------------------------------------------+\n  | Dump env=(empty), code=(empty)                                      |\n  +---------------------------------------------------------------------+\n```\n\nもう一度Returnになっているので、同じ処理を行う。\n\n```\ncode:\n  (empty)  \u003c-- Dump から上書き\n\nenv:\n  (empty)  \u003c-- Dump から上書き\n\nstack:\n  +---------------------------------------------------------------------+\n  | Closure y, env=x:\u003ccallure\u003e, code=[Fetch x ; Return]                 |\n  +---------------------------------------------------------------------+\n```\n\nコードがなくなったので、Case.3 と同じくClosureの内部の簡約になる。\nスタックにはClosureのArgをSymbolとしてPushし、\ncodeにAbstractを挿入してDumpをPushする。\n\n```\ncode:\n  Fetch x\n  Return\n\nenv:\n  x = Callure [ Fetch x ; Fetch z ; Apply ; Return ]\n\nstack:\n  +---------------------------------------------------------------------+\n  | Dump env=(empty), code=[Abstract]                                   |\n  +---------------------------------------------------------------------+\n  | Symbol(y)                                                           |\n  +---------------------------------------------------------------------+\n```\n\n実行される Fetch でenvを参照し、スタックにCallureをPushする。\n\n```\ncode:\n  Return\n\nenv:\n  x = Callure [ Fetch x ; Fetch z ; Apply ; Return ]\n\nstack:\n  +---------------------------------------------------------------------+\n  | Callure [ Fetch x ; Fetch z ; Apply ; Return ]                      |\n  +---------------------------------------------------------------------+\n  | Dump env=(empty), code=[Abstract]                                   |\n  +---------------------------------------------------------------------+\n  | Symbol(y)                                                           |\n  +---------------------------------------------------------------------+\n```\n\nReturnはスタックの先頭がCallureの場合、Callureの展開を行う。\nこの処理は少しややこしい。\n\n0. codeがReturnで、スタックの先頭がCallureの場合\n1. codeにReturnを挿入する（上の0.のReturnは消費してしまったため、代わりに追加）\n2. この状態でcodeとenvの内容を、Dumpに詰め込んで、スタックにPushする\n3. envとcodeを、Callureの保持している内容で上書きする\n\nその結果、今度はenvに参照がない Fetch x となるので、単純にSymbolとして\nスタックにPushする。Fetch z も同じ。\n\n```\ncode:\n  Fetch x\n  Fetch z\n  Apply\n  Return\n\nenv:\n  (empty)\n\nstack:\n  +---------------------------------------------------------------------+\n  | Dump env=x:\u003ccallure\u003e code=[Return]                                  |\n  +---------------------------------------------------------------------+\n  | Dump env=(empty), code=[Abstract]                                   |\n  +---------------------------------------------------------------------+\n  | Symbol(y)                                                           |\n  +---------------------------------------------------------------------+\n```\n\n次のApplyは単純にApplicationを作成する。\n\n```\ncode:\n  Apply\n  Return\n\nenv:\n  (empty)\n\nstack:\n  +---------------------------------------------------------------------+\n  | Symbol(z)                                                           |\n  +---------------------------------------------------------------------+\n  | Symbol(x)                                                           |\n  +---------------------------------------------------------------------+\n  | Dump env=x:\u003ccallure\u003e code=[Return]                                  |\n  +---------------------------------------------------------------------+\n  | Dump env=(empty), code=[Abstract]                                   |\n  +---------------------------------------------------------------------+\n  | Symbol(y)                                                           |\n  +---------------------------------------------------------------------+\n```\n\ncodeの先頭がReturnになるので、一つ目のDumpを取り出す。\n\n```\ncode:\n  Return\n\nenv:\n  (empty)\n\nstack:\n  +---------------------------------------------------------------------+\n  | Application(x z)                                                    |\n  +---------------------------------------------------------------------+\n  | Dump env=x:\u003ccallure\u003e code=[Return]                                  |\n  +---------------------------------------------------------------------+\n  | Dump env=(empty), code=[Abstract]                                   |\n  +---------------------------------------------------------------------+\n  | Symbol(y)                                                           |\n  +---------------------------------------------------------------------+\n```\n\nenvとcodeを上書きすると、またReturnが残る。次のDumpを取り出す。\n\n```\ncode:\n  Return\n\nenv:\n  x = Callure [ Fetch x ; Fetch z ; Apply ; Return ]\n\nstack:\n  +---------------------------------------------------------------------+\n  | Application(x z)                                                    |\n  +---------------------------------------------------------------------+\n  | Dump env=(empty), code=[Abstract]                                   |\n  +---------------------------------------------------------------------+\n  | Symbol(y)                                                           |\n  +---------------------------------------------------------------------+\n```\n\n最後にAbstractが残るので、Case.3 と同じ動作になる。\n\n```\ncode:\n  Abstract\n\nenv:\n  (empty)\n\nstack:\n  +---------------------------------------------------------------------+\n  | Application(x z)                                                    |\n  +---------------------------------------------------------------------+\n  | Symbol(y)                                                           |\n  +---------------------------------------------------------------------+\n```\n\nスタックの先頭を本体とし、スタックの次のSymbolをラムダ変数とした\nFunctionを生成して、スタックにPushする。\n\n```\ncode:\n  (empty)\n\nenv:\n  (empty)\n\nstack:\n  +---------------------------------------------------------------------+\n  | Function{ Arg=y, Body=(x z) }                                       |\n  +---------------------------------------------------------------------+\n```\n\nこれにより次の結果が得られる。\n\n```\n元のラムダ式\n(^x.^y.x (x z))\n\n結果\n^y.(x z)\n```\n\n\n## 問題点\n\n簡約後に残る文字列から、元の変数の束縛が分からない。\n簡約後の文字列は「正確ではない」ということになる。\n\n例を挙げる。下記の場合、yの下に振った数字の通り、y1 と y2 は\n異なる変数であるが、\n\n```\n(^x.^y.(x y) y)\n     2    2  1\n```\n\n簡約後に下記のようになった文字列を見ても判断ができない。\n\n```\n^y.(y y)\n 2  1 2\n```\n\nこれは自由変数が含まれると発生する。\n変数はすべて関数のラムダ変数として束縛し、\n何も束縛がない自由変数を許さない、という制限があれば、\nこの問題は発生しない。\n\n\n## 参考\n\n- https://tomstu.art/programming-with-nothing\n- https://tomstu.art/programming-with-something\n\nとくに後者の `Making it even faster still` の rubyの実装を最初に見た。\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finazak%2Flambda-calculus-machine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finazak%2Flambda-calculus-machine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finazak%2Flambda-calculus-machine/lists"}