{"id":20425357,"url":"https://github.com/catseye/yolk","last_synced_at":"2025-04-12T18:55:18.440Z","repository":{"id":20014223,"uuid":"23281854","full_name":"catseye/Yolk","owner":"catseye","description":"MIRROR of https://codeberg.org/catseye/Yolk : A tiny S-expression language with a tiny self-interpreter","archived":false,"fork":false,"pushed_at":"2023-10-25T12:39:34.000Z","size":37,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-26T13:11:53.127Z","etag":null,"topics":["esolang","esoteric-language","esoteric-programming-language","meta-circular","s-expressions","self-interpreter"],"latest_commit_sha":null,"homepage":"https://catseye.tc/node/Yolk","language":"Python","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/catseye.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}},"created_at":"2014-08-24T13:37:06.000Z","updated_at":"2023-10-24T21:10:33.000Z","dependencies_parsed_at":"2022-08-27T02:33:10.161Z","dependency_job_id":null,"html_url":"https://github.com/catseye/Yolk","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FYolk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FYolk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FYolk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FYolk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/catseye","download_url":"https://codeload.github.com/catseye/Yolk/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248618262,"owners_count":21134200,"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":["esolang","esoteric-language","esoteric-programming-language","meta-circular","s-expressions","self-interpreter"],"created_at":"2024-11-15T07:13:01.883Z","updated_at":"2025-04-12T18:55:18.419Z","avatar_url":"https://github.com/catseye.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Yolk\n====\n\nVersion 1.0\n| _Wiki entry_ [@ esolangs.org](https://esolangs.org/wiki/Yolk)\n| _See also:_ [Pixley](https://codeberg.org/catseye/Pixley#pixley)\n∘ [Exanoke](https://codeberg.org/catseye/Exanoke#exanoke)\n\n- - - -\n\nYolk is a programming language (or computational calculus) with a very\nsmall meta-circular definition.\n\nA Yolk program consists of an S-expression.  This gives the body of a\nfunction which takes an S-expression as its argument and which evaluates to\nan S-expression.\n\nIn this context, an S-expression is an atom or a list of S-expressions.\n\nAn atom is one of the following seven atomic symbols:\n`ifeq`, `quote`, `head`, `tail`, `cons`, `arg`, or `self`.\n\nBasic Semantics\n---------------\n\n    -\u003e Tests for functionality \"Evaluate Yolk program\"\n\n`(quote A)` evaluates to A.  (It does not evaluate A.)\n\n    | (quote cons)\n    = cons\n\n    | (quote (cons (head cons tail) cons))\n    = (cons (head cons tail) cons)\n\n`(head A)` evaluates A to a list and itself evaluates to first element of the\nlist.\n\n    | (head (quote (cons tail tail)))\n    = cons\n\nIf A is not a list, `(head A)`, crashes.\n\n    | (head (quote head))\n    ? \n\n`(tail A)` evaluates A to a list and itself evaluates to everything except the\nfirst element of the list.\n\n    | (tail (quote (cons head head)))\n    = (head head)\n\nIf A is not a list, `(tail A)` crashes.\n\n    | (tail (quote tail))\n    ? \n\n`(cons A B)` evaluates A to a S-expression and B to a list, and evaluates to a list\nwhich is the same as B except it has an extra element on the front, A.\n\n    | (cons (quote quote) (quote (head tail cons)))\n    = (quote head tail cons)\n\n`(ifeq A B C D)` evaluates A and B; if they evaluate to the same atom\nthen this form evaluates to what C evaluates to, otherwise it evaluates to\nwhat D evaluates to.\n\n    | (ifeq (quote head) (quote head) (quote cons) (quote self))\n    = cons\n\n    | (ifeq (quote head) (quote tail) (quote cons) (quote self))\n    = self\n\nAdvanced semantics\n------------------\n\n`arg` evaluates to the argument of the current program-function.\n\n    | arg\n    + ifeq\n    = ifeq\n\n    | (tail arg)\n    + (ifeq ifeq (ifeq ifeq))\n    = (ifeq (ifeq ifeq))\n\n`(self A)` evaluates A to an S-expression, then evaluates the main\nprogram-function with that as the argument, and evaluates to the result\nof that.\n\n    | (ifeq (head arg) (quote cons) arg (self (tail arg)))\n    + (head head cons tail tail)\n    = (cons tail tail)\n\nMeta-circular Interpreter\n-------------------------\n\n### Preliminary Sketch ###\n\nI believe it is possible to write a meta-circular interpreter for Yolk.\n\nWe'll start with a sketch of it, which is not a full interpreter but which\ngets the basic idea across.  The sketch looks like this:\n\n    (ifeq arg (quote arg)\n        arg\n        (ifeq (head arg) (quote head)\n            (head (self (head (tail arg))))\n            (ifeq (head arg) (quote tail)\n                (tail (self (head (tail arg))))\n                (ifeq (head arg) (quote cons)\n                    (cons (self (head (tail arg))) (self (head (tail (tail arg)))))\n                    (ifeq (head arg) (quote quote)\n                        (head (tail arg))\n                        (ifeq (head arg) (quote ifeq)\n                            (ifeq (self (head (tail arg)))\n                                (self (head (tail (tail arg))))\n                                (self (head (tail (tail (tail arg)))))\n                                (self (head (tail (tail (tail (tail arg)))))))\n                            (ifeq (head arg) (quote self)\n                                (self (self (head (tail arg))))\n                                (ifeq))))))))\n\nThe `(ifeq)` at the end there is just there to trigger a runtime error if\nthings go badly.\n\nWe test this meta-circular interpreter on the previous illustrations of the\nbasic semantics as follows.  The interpreter itself is sourced from a file to\nreduce repetition in this document.\n\n    -\u003e Tests for functionality \"Evaluate Yolk program with MCI Sketch\"\n\n    | (quote cons)\n    = cons\n\n    | (quote (cons (head cons tail) cons))\n    = (cons (head cons tail) cons)\n\n    | (head (quote (cons tail tail)))\n    = cons\n\n    | (head (quote head))\n    ? \n\n    | (tail (quote (cons head head)))\n    = (head head)\n\n    | (tail (quote tail))\n    ? \n\n    | (cons (quote quote) (quote (head tail cons)))\n    = (quote head tail cons)\n\n    | (ifeq (quote head) (quote head) (quote cons) (quote self))\n    = cons\n\n    | (ifeq (quote head) (quote tail) (quote cons) (quote self))\n    = self\n\n### With Input ###\n\n    -\u003e Tests for functionality \"Evaluate Yolk program with MCI with arg\"\n\nHowever, there is a small problem.  Where does this interpreter get its\ninput from, for `arg`?\n\nInstead of having the input to the MCI just be the program to be interpreted,\nit must be extended: a 2-element list where the first element is the program\nand the second is the input.\n\nIn our first step towards doing this, we will just package the program and\nits input into this list, but still ignore the input.  We change the MCI to\nhandle this format by\n\n*   replacing `arg`, where we read it, with `(head arg)`, and\n*   replacing `arg`, where we evaluate to it, with `(head (tail arg))`, and\n*   replacing the modified `arg`, where we pass it to `self`,\n    e.g. `(self (head (tail arg)))`, with\n    `(cons (head (tail (head arg))) (tail arg))`\n\nAnd we get:\n\n    (ifeq (head arg) (quote arg)\n        (head (tail arg))\n        (ifeq (head (head arg)) (quote head)\n            (head (self (cons (head (tail (head arg))) (tail arg))))\n            (ifeq (head (head arg)) (quote tail)\n                (tail (self (cons (head (tail (head arg))) (tail arg))))\n                (ifeq (head (head arg)) (quote cons)\n                    (cons (self (cons (head (tail (head arg))) (tail arg))) (self (cons (head (tail (tail (head arg)))) (tail arg))))\n                    (ifeq (head (head arg)) (quote quote)\n                        (head (tail (head arg)))\n                        (ifeq (head (head arg)) (quote ifeq)\n                            (ifeq (self (cons (head (tail (head arg))) (tail arg)))\n                                  (self (cons (head (tail (tail (head arg)))) (tail arg)))\n                                  (self (cons (head (tail (tail (tail (head arg))))) (tail arg)))\n                                  (self (cons (head (tail (tail (tail (tail (head arg)))))) (tail arg))))\n                            (ifeq (head (head arg)) (quote self)\n                                (self (self (head (tail arg))))\n                                (ifeq))))))))\n\nNow we test it.\n\n`quote` in MCI.\n\n    | ((quote ifeq) cons)\n    = ifeq\n\n    | ((quote (ifeq (head ifeq tail) ifeq)) cons)\n    = (ifeq (head ifeq tail) ifeq)\n\n`head` in MCI.\n \n    | ((head (quote (ifeq tail tail))) cons)\n    = ifeq\n\n`tail` in MCI.\n\n    | ((tail (quote (cons head head))) cons)\n    = (head head)\n \n`cons` in MCI.\n \n    | ((cons (quote quote) (quote (head tail ifeq))) ifeq)\n    = (quote head tail ifeq)\n\n`ifeq` in MCI.\n\n    | ((ifeq (quote head) (quote head) (quote cons) (quote self)) ifeq)\n    = cons\n\n    | ((ifeq (quote head) (quote tail) (quote cons) (quote self)) ifeq)\n    = self\n\n`arg` in MCI.\n\n    | (arg ifeq)\n    = ifeq\n\n    | ((tail arg) (ifeq ifeq (ifeq ifeq)))\n    = (ifeq (ifeq ifeq))\n\n### With Recursion ###\n\n    -\u003e Tests for functionality \"Evaluate Yolk program\"\n\nNow the final stroke — fix the implementation of `self`.  This is kind of\ntricky.\n\nIn the MCI, `self` is used for recursively interpreting parts of the \"target\nprogram\", i.e. the program given in the head of the arg.\n\nIn the program being interpreted, `self` is used for recursing *that* program.\n\nSo, we evaluate the first argument to a value:\n    \n    (self (cons (head (tail (head arg))) (tail arg)))\n\nThen we want to evaluate the target program, all of it, on that value.\n\nBut wait!\n\nWe don't have a way to evaluate the entire target program!  We don't even *have*\nthe entire target program!  We'll have to keep a copy around, so instead of a\n2-element list, we'll need a 3-element list (I knew that.)\n\n    | (ifeq (head arg) (quote arg)\n    |     (head (tail (tail arg)))\n    |     (ifeq (head (head arg)) (quote head)\n    |         (head (self (cons (head (tail (head arg))) (tail arg))))\n    |         (ifeq (head (head arg)) (quote tail)\n    |             (tail (self (cons (head (tail (head arg))) (tail arg))))\n    |             (ifeq (head (head arg)) (quote cons)\n    |                 (cons (self (cons (head (tail (head arg))) (tail arg))) (self (cons (head (tail (tail (head arg)))) (tail arg))))\n    |                 (ifeq (head (head arg)) (quote quote)\n    |                     (head (tail (head arg)))\n    |                     (ifeq (head (head arg)) (quote ifeq)\n    |                         (ifeq (self (cons (head (tail (head arg))) (tail arg)))\n    |                               (self (cons (head (tail (tail (head arg)))) (tail arg)))\n    |                               (self (cons (head (tail (tail (tail (head arg))))) (tail arg)))\n    |                               (self (cons (head (tail (tail (tail (tail (head arg)))))) (tail arg))))\n    |                         (ifeq (head (head arg)) (quote self)\n    |                             (self (self (head (tail arg))))\n    |                             (ifeq))))))))\n    + (\n    +   (ifeq arg (quote tail) (quote cons) (quote self))\n    +   (ifeq arg (quote tail) (quote cons) (quote self))\n    +   tail\n    + )\n    = cons\n\n    | (ifeq (head arg) (quote arg)\n    |     (head (tail (tail arg)))\n    |     (ifeq (head (head arg)) (quote head)\n    |         (head (self (cons (head (tail (head arg))) (tail arg))))\n    |         (ifeq (head (head arg)) (quote tail)\n    |             (tail (self (cons (head (tail (head arg))) (tail arg))))\n    |             (ifeq (head (head arg)) (quote cons)\n    |                 (cons (self (cons (head (tail (head arg))) (tail arg))) (self (cons (head (tail (tail (head arg)))) (tail arg))))\n    |                 (ifeq (head (head arg)) (quote quote)\n    |                     (head (tail (head arg)))\n    |                     (ifeq (head (head arg)) (quote ifeq)\n    |                         (ifeq (self (cons (head (tail (head arg))) (tail arg)))\n    |                               (self (cons (head (tail (tail (head arg)))) (tail arg)))\n    |                               (self (cons (head (tail (tail (tail (head arg))))) (tail arg)))\n    |                               (self (cons (head (tail (tail (tail (tail (head arg)))))) (tail arg))))\n    |                         (ifeq (head (head arg)) (quote self)\n    |                             (self (self (head (tail arg))))\n    |                             (ifeq))))))))\n    + (\n    +   (ifeq arg (quote tail) (quote cons) (quote self))\n    +   (ifeq arg (quote tail) (quote cons) (quote self))\n    +   head\n    + )\n    = self\n\nWe'll also need some way to evaluate that entire client program.  Well, we have ourselves,\nthe MCI, so that should be OK.   ...right?  Let's try again.\n\nWe evaluate the first argument to a value:\n\n    let val = (self (cons (head (tail (head arg))) (tail arg)))\n\nThen we get the target program:\n\n    let pgm = (head (tail arg))\n\nThen we evaluate the target program, all of it, on that value.\n\n    (self (list pgm pgm val))\n\nOf course we don't have `list` so we say\n\n    (self (cons pgm (cons pgm (cons val ()))))\n\nNor do we have `()` so\n\n    (self (cons pgm (cons pgm (cons val (tail (quote (tail)))))))\n\nNor do we have `let` so\n\n    (self (cons (head (tail arg))\n            (cons (head (tail arg))\n              (cons (self (cons (head (tail (head arg))) (tail arg))) (tail (quote (tail)))))))\n\nAnd we hold our breath and:\n\n    | (ifeq (head arg) (quote arg)\n    |     (head (tail (tail arg)))\n    |     (ifeq (head (head arg)) (quote head)\n    |         (head (self (cons (head (tail (head arg))) (tail arg))))\n    |         (ifeq (head (head arg)) (quote tail)\n    |             (tail (self (cons (head (tail (head arg))) (tail arg))))\n    |             (ifeq (head (head arg)) (quote cons)\n    |                 (cons (self (cons (head (tail (head arg))) (tail arg))) (self (cons (head (tail (tail (head arg)))) (tail arg))))\n    |                 (ifeq (head (head arg)) (quote quote)\n    |                     (head (tail (head arg)))\n    |                     (ifeq (head (head arg)) (quote ifeq)\n    |                         (ifeq (self (cons (head (tail (head arg))) (tail arg)))\n    |                               (self (cons (head (tail (tail (head arg)))) (tail arg)))\n    |                               (self (cons (head (tail (tail (tail (head arg))))) (tail arg)))\n    |                               (self (cons (head (tail (tail (tail (tail (head arg)))))) (tail arg))))\n    |                         (ifeq (head (head arg)) (quote self)\n    |                             (self (cons (head (tail arg))\n    |                                     (cons (head (tail arg))\n    |                                       (cons (self (cons (head (tail (head arg))) (tail arg))) (tail (quote (tail)))))))\n    |                             (ifeq))))))))\n    + (\n    +   (ifeq (head arg) (quote ifeq) arg (self (tail arg)))\n    +   (ifeq (head arg) (quote ifeq) arg (self (tail arg)))\n    +   (head head ifeq tail tail)\n    + )\n    = (ifeq tail tail)\n\nHuzzah!  Now we test all the things again...\n\n    -\u003e Tests for functionality \"Evaluate Yolk program with MCI\"\n\n`(quote A)` evaluates to A.  (It does not evaluate A.)\n\n    | ((quote ifeq) (quote ifeq) cons)\n    = ifeq\n\n    | ((quote (cons (head cons tail) cons)) (quote (cons (head cons tail) cons)) ifeq)\n    = (cons (head cons tail) cons)\n\n`(head A)` evaluates A to a list and itself evaluates to first element of the\nlist.\n\n    | ((head (quote (cons tail tail))) (head (quote (cons tail tail))) ifeq)\n    = cons\n\nIf A is not a list, `(head A)` crashes.\n\n    | ((head (quote head)) (head (quote head)) cons)\n    ? \n\n`(tail A)` evaluates A to a list and itself evaluates to everything except the\nfirst element of the list.\n\n    | ((tail (quote (cons head head))) (tail (quote (cons head head))) ifeq)\n    = (head head)\n\nIf A is not a list, `(tail A)` crashes.\n\n    | ((tail (quote tail)) (tail (quote tail)) ifeq)\n    ? \n\n`(cons A B)` evaluates A to a S-expression and B to a list, and evaluates to a list\nwhich is the same as B except it has an extra element on the front, B.\n\n    | ((cons (quote quote) (quote (head tail ifeq))) (cons (quote quote) (quote (head tail ifeq))) ifeq)\n    = (quote head tail ifeq)\n\n`(ifeq A B C D)` evaluates A and B; if they evaluate to the same atom\nthen this form evaluates to what C evaluates to, otherwise it evaluates to\nwhat D evaluates to.\n\n    | ((ifeq (quote head) (quote head) (quote cons) (quote self))\n    |  (ifeq (quote head) (quote head) (quote cons) (quote self)) tail)\n    = cons\n\n    | ((ifeq (quote head) (quote tail) (quote cons) (quote self))\n    |  (ifeq (quote head) (quote tail) (quote cons) (quote self)) tail)\n    = self\n\n`arg` evaluates to the argument of the current program-function.\n\n    | (arg arg ifeq)\n    = ifeq\n\n    | ((tail arg) (tail arg) (ifeq ifeq (ifeq ifeq)))\n    = (ifeq (ifeq ifeq))\n\n`(self A)` evaluates A to an S-expression, then evaluates the main\nprogram-function with that as the argument, and evaluates to the result\nof that.\n\n    | ((ifeq (head arg) (quote ifeq) arg (self (tail arg)))\n    |  (ifeq (head arg) (quote ifeq) arg (self (tail arg)))\n    |  (head head ifeq tail tail))\n    = (ifeq tail tail)\n\nDiscussion\n----------\n\n### Comparison to Pixley ###\n\nYolk is quite similar to [Pixley][].  The main differences between Pixley and\nYolk are:\n\n*   Yolk is not a subset of R5RS Scheme\n*   Yolk was designed in a completely different fashion\n\nIn the first version of Yolk, `if` and `eq` were different forms.  I wrote\na meta-circular interpreter with that version, and measured it using `stats.scm`\nfrom the Pixley distribution:\n\n    Cons cells: 296\n    Symbol instances: 169\n    Unique symbols: 8 (cons self tail quote arg head eq if)\n\nThen I realized that the first argument of `if` was always an `eq`, so merged\nthem into `ifeq`, and that the MCI could be simplified a tiny bit for the error\ncase, and that version is what is presented here.  It measures out to be:\n\n    Cons cells: 255\n    Symbol instances: 146\n    Unique symbols: 7 (cons self tail quote arg head ifeq)\n\nI then noticed that the second argument to `if` was always a `quote`, but\nrealized, when trying to change it so that it did not evaluate its second\nargument, that it would no longer be possible to write an MCI in the resulting\nlanguage (explaining why this is, is left as an exercise for the reader.)\n\nFor comparison, the latest version of `pixley.pix`, as of this writing, has\n\n    Cons cells: 684\n    Symbol instances: 413\n    Unique symbols: 54\n\nSince Yolk is very similar to Pixley, but has a much smaller meta-circular\ndefinition, an argument could definitely be made that its name should be\nHooterville — but that's not such a great name for a programming language.\n\nOn the other hand, the Yolk MCI has a less interesting (IMO) depiction as\nnested rectangles a la Pixley.  That is not to say there isn't some possible\ngraphical depiction that makes the Yolk MCI look pretty, of course.\n\n### Computational class ###\n\nYolk is almost certainly Turing-complete.  That, in itself, is not terribly\ninteresting; what is more interesting is how we can come to that conclusion\nwithout the usual devices (giving a map from Yolk programs to Turing machines,\nor implementing a universal Turing machine in Yolk.)\n\nYolk came about from my interest in whether there are any universal\ncomputational classes which are not Turing-complete.  \"Universal\" in this\nsense means something like, if M is in class C and M can simulate any other\nmember of class C then M is universal.\n\nWe know that there is a universal Turing machine.  We also know that there\nis no universal primitive recursive function.  (There is a total function\nwhich can simulate any PR function, but that function isn't itself PR.)\n\nAnd we haven't yet found a computational class that is contained in PR that\nis universal; they all admit to various diagonalizations and pumping lemmas\nand such, and the function that can simulate any member of C is always, it\nseems, somewhere outside C.\n\nBut are there any classes between PR and RE (the class of Turing machines)\nthat are universal?  It seems likely.\n\nIn fact, R (the class of Turing machines which always halt) qualifies in some\nsense: if I always give a UTM only descriptions of TMs that always halt, then\nthat UTM always halts.  (Of course, that doesn't address the question of how\nthat UTM would prevent itself from trying to simulate a TM that doesn't halt\nif it were given one.)\n\nIn a similar vein, it seems possible that linear-bounded automata might be\nuniversal: if it takes a UTM at most (say) 18 steps to simulate one TM\ntransition, then, given a description of a linear-bounded TM on input, the\nUTM's run time should bounded linearly too.  (Well, maybe.  The input on which\nthe bounds are based is now (data+machine_description) instead of just\n(data)... anyway I haven't thought about it very much and it is more or less\na distraction from my main point.)\n\nThe thing is that PR and RE both have simple, \"syntactic\" definitions.\nYou can tell a PR computation or a RE computation just from its surface\nstructure.  But every class between them has a more complex, \"semantic\"\ndefinition.  You need some extra machinery to tell if the machine being\nsimulated really belongs to the class or not — you need to prove it always\nhalts, or prove that it finishes within a linear bound of its input, or\nwhatnot.\n\nYolk's meta-circular interpreter can interpret itself, and, because it is\nwritten in a general way, it can interpret any other Yolk program, too, making\nit a universal Yolk interpreter.  Yet it has none of this extra machinery\nthat would be required for it to be above PR yet below RE.  So the functions\nit can interpret must be all of those in RE; i.e. Yolk is Turing-complete.\n\nTHAT IS, OF COURSE, NOT A PROOF.\n\nBut on the other hand you can look at the Yolk instructions and probably\nsatisfy yourself that it would not be difficult to write a Tag machine in\nYolk.  It can locate data (`arg`, `head`, `tail`, `quote`), it can make\ndecisions based on that data (`ifeq`), and it can repeat this process with\ndifferent data (`self`).\n\nIn fact, the interesting thing is `cons`.\n\n### `cons` ###\n\nWe can, in fact, re-write the initial sketch of the self-interpreter without\nhandling `cons` at all, because `cons` is only used in the definition of\n`cons`:\n\n    (ifeq arg (quote arg)\n        arg\n        (ifeq (head arg) (quote head)\n            (head (self (head (tail arg))))\n            (ifeq (head arg) (quote tail)\n                (tail (self (head (tail arg))))\n                (ifeq (head arg) (quote quote)\n                    (head (tail arg))\n                    (ifeq (head arg) (quote ifeq)\n                        (ifeq (self (head (tail arg)))\n                            (self (head (tail (tail arg))))\n                            (self (head (tail (tail (tail arg)))))\n                            (self (head (tail (tail (tail (tail arg)))))))\n                        (ifeq (head arg) (quote self)\n                            (self (self (head (tail arg))))\n                            (ifeq))))))))\n\nAnd you'd think, maybe, this can interpret itself.  And, maybe, that because\nit works on smaller and smaller data each time, it is primitive recursive\n(a la [Exanoke][].)  Except, no.  The definition of `self` in the sketch is wrong,\nreally quite wrong.  And to fix it, you need to be able to hold multiple\ndata in `arg`, and take it apart to find the code vs. the real data, and put\nit back together again — and to put it back together again, you need `cons`.\n\nOr something like `cons`, obviously; I played with various variations but\nrealized they all allow you to \"grow\" an S-expression.\n\nNow, maybe I just didn't play with them *enough*.  Maybe we can define some\nsort of `cons` that imposes some sort of restriction on its result not being\n\"bigger\" than its input, except when used a certain way, somehow, in `self`\nespecially, so that... it all works out and it's universal but it's not\nTuring-complete.\n\nBut I would be willing to bet that the meta-circular interpreter for that,\neven if it exists, would be quite a bit larger than this.\n\n(Happy Happy)!  \nChris Pressey  \nLondon, UK  \nAugust 24th, 2014\n\n[Pixley]: https://catseye.tc/node/Pixley\n[Exanoke]: https://catseye.tc/node/Exanoke\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatseye%2Fyolk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcatseye%2Fyolk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatseye%2Fyolk/lists"}