{"id":20425274,"url":"https://github.com/catseye/wagon","last_synced_at":"2026-04-19T11:32:26.639Z","repository":{"id":142240023,"uuid":"202717265","full_name":"catseye/Wagon","owner":"catseye","description":"MIRROR of https://codeberg.org/catseye/Wagon : A second-order concatenative language","archived":false,"fork":false,"pushed_at":"2023-12-12T20:34:38.000Z","size":16,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-15T15:11:56.922Z","etag":null,"topics":["concatenative-programming-language","esolang","esoteric-programming-language","purely-concatenative","second-order"],"latest_commit_sha":null,"homepage":"https://catseye.tc/node/Wagon","language":"Haskell","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","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":null,"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":"2019-08-16T11:38:54.000Z","updated_at":"2023-10-25T08:31:15.000Z","dependencies_parsed_at":"2025-01-15T15:08:51.436Z","dependency_job_id":"2007f829-75e0-4edf-89f3-037bf65a9b7c","html_url":"https://github.com/catseye/Wagon","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FWagon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FWagon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FWagon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FWagon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/catseye","download_url":"https://codeload.github.com/catseye/Wagon/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241966977,"owners_count":20050324,"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":["concatenative-programming-language","esolang","esoteric-programming-language","purely-concatenative","second-order"],"created_at":"2024-11-15T07:12:45.189Z","updated_at":"2026-04-19T11:32:26.571Z","avatar_url":"https://github.com/catseye.png","language":"Haskell","readme":"Wagon\n=====\n\n_Try it online_ [@ catseye.tc](https://catseye.tc/installation/Wagon)\n| _Wiki entry_ [@ esolangs.org](https://esolangs.org/wiki/Wagon)\n| _See also:_ [Carriage](https://codeberg.org/catseye/Carriage#carriage)\n∘ [Equipage](https://codeberg.org/catseye/Equipage#equipage)\n∘ [Oxcart](https://codeberg.org/catseye/Oxcart#oxcart)\n\n- - - -\n\nIntroduction\n------------\n\nIn a conventional concatenative language, every symbol represents\na function which takes program states to program states, and the program\nis a concatenation (sequential composition) of such functions.\nThis is fine when it comes to manipulating state, but what about control?\n\nOne can allow individual functions to be defined and named seperately,\nthen applied, perhaps conditionally and perhaps recursively.  While\nthis is conventional in the world of concatenative languages, such\nlanguages are arguably no longer purely concatenative (a subject\nexplored by [Carriage][] and [Equipage][]).\n\nIf one wishes the language to remain purely concatenative, it seems one\nmust store control structures in the state (for example, [Equipage][]\nstores functions on the stack) or, more drastically, allow the function\nthat is being constructed, to be examined somehow during the construction\nprocess.  Functions typically aren't considered examinable in this way,\nbut even so, examining it this way is not very different from putting\na copy of the program itself in the state.\n\n**Wagon** is an experiment with a third option: *second-order functions*.\nInstead of functions which take states to states, the symbols of the\nlanguage represent functions which take functions from states to states,\nto functions that take states to states.  The state is merely a stack of\nintegers, and the program is purely a concatenation of functions.\n\nMy hope was that these second-order functions could express control in\naddition to expressing state manipulation.  This does turn out to be the\ncase, but only to a degree.  As far as I have been able to determine,\nthey can express some control structures, but not arbitrary ones.\n\nFundamentals of Wagon\n---------------------\n\nTalking about functions that themselves work on functions is admittedly somewhat\nawkward, so let's define some terms.  Let's call a function that takes states\nto states an _operation_.  Let's call a function that takes operations to\noperations a _macro_.  A Wagon program, then, consists of a sequence of symbols,\neach of which represents a macro.  These individual macros are concatenated\n(sequentially composed) to form a single macro.\n\nSince a macro takes operations to operations, it's not possible to \"run\" a macro\ndirectly.  So, when asked to \"run\" or \"evaluate\" a Wagon program,\nwe take the following convention: apply the macro that the Wagon program represents\nto the identity function, and apply the resulting function to an initial stack.\nWe may allow a Wagon program to accept input by supplying it on the initial stack,\nbut usually the initial stack is just empty.  The output is usually a depiction\nof the final state of the stack.\n\nMany of the primitive macros in Wagon are based directly on operations.  There are\ntwo main ways we \"lift\" an operation _k_ to a macro.  The first is a macro which takes\nan operation _o_ and returns an operation which performs _o_ then performs _k_ —\nwe call this sort of macro an _after-lifting_ of _k_.  The second is a macro which\ntakes an operation _o_ and returns an operation which performs _k_ then performs _o_ —\nwe call this sort of macro a _before-lifting_ of _k_.\n\nWagon's convention is that after-lifted operations are represented by lowercase letters,\nand before-lifted operations are represented by uppercase letters.  Macros which do not\nclearly fall into either of these two categories are represented by punctuation symbols.\n\nPrimitive Macros of Wagon\n-------------------------\n\n    -\u003e Tests for functionality \"Evaluate Wagon Program\"\n\n    -\u003e Functionality \"Evaluate Wagon Program\" is implemented by\n    -\u003e shell command \"bin/wagon run %(test-body-file)\"\n\n    -\u003e Functionality \"Evaluate Wagon Program\" is implemented by\n    -\u003e shell command \"bin/wagon eval %(test-body-file)\"\n\nNote that in the following examples, the `===\u003e` line is not part of the program;\ninstead it shows the expected result of running each program.  In the expected\nresult, the resulting stack is depicted top-to-bottom.\n\n### Rudimentary Arithmetic ###\n\n`i` is a macro which takes an operation _o_ and returns an operation which\nperforms _o_ then pushes a 1 onto the stack.\n\n`s` is a macro which takes an operation _o_ and returns an operation which\nperforms _o_ then pops _a_ from the stack then pops _b_ from the stack and\npushes _b_ - _a_.\n\nWith these we can construct some numbers.\n\n    i\n    ===\u003e [1]\n\n    iis\n    ===\u003e [0]\n\n    iis is\n    ===\u003e [-1]\n\n    i iis is s\n    ===\u003e [2]\n\nAs you can see, `i` and `s` are after-lifted operations.  There are also\nbefore-lifted counterparts of them:\n\n`I` is a macro which takes an operation _o_ and returns an operation which\npushes a 1 onto the stack then performs _o_.\n\n`S` is a macro which takes an operation _o_ and returns and operation which\npops _a_ from the stack then pops _b_ from the stack and pushes _b_ - _a_\nthen performs _o_.\n\n    SII\n    ===\u003e [0]\n\nNote also that whitespace maps to the identity function (a macro which takes\nan operation and returns that same operation) so is effectively insignificant\nin a Wagon program.\n\n### Rudimentary Stack Manipulation ###\n\n`p` is an after-lifting of the operation: pop a value from the stack and discard it.\n\n    i iis iis iis ppp\n    ===\u003e [1]\n\n`P` is the before-lifted counterpart to `p`.\n\n    PI\n    ===\u003e []\n\n`d` is an after-lifting of the operation: duplicate the top value on the stack.\n\n    iis ddd\n    ===\u003e [0,0,0,0]\n\n`D` is the before-lifted counterpart to `d`.\n\n    DDDI\n    ===\u003e [1,1,1,1]\n\n### Sophisticated Stack Manipulation ###\n\n`r` is an after-lifted operation: it pops a value _n_ from the stack. Then it\npops _n_ values from the stack and temporarily remembers them.  Then it\nreverses the remainder of the stack.  Then it pushes those _n_ remembered\nvalues back onto the stack.  _n_ must be zero or one.\n\n    iis i iiisiss\n    ===\u003e [2,1,0]\n\n    iis i iiisiss   iis r\n    ===\u003e [0,1,2]\n\n    iis i iiisiss   i r\n    ===\u003e [2,0,1]\n\n`R` is the before-lifted counterpart to `r`.\n\n    I SII\n    ===\u003e [1,0]\n\n    R SII I SII\n    ===\u003e [0,1]\n\n### Rudimentary Control Flow ###\n\n`@` (pronounced \"while\") is a macro which takes an operation _o_ and returns\nan operation that repeatedly performs _o_ as long as there are elements on\nthe stack and the top element of the stack is non-zero.\n\n    p@ I I I SII SII\n    ===\u003e [0,0]\n\nAn \"if\" can be constructed by writing a \"while\" that, first thing it does is,\npop the non-zero it detected, and last thing it does is, push a 0 onto the\nstack.  Then immediately afterwards, pop the top element of the stack (which\nwe know must be zero because we know the loop just exited, whether it was\nexecuted once, or not at all.)\n\nComputational Class\n-------------------\n\nWhen Wagon was first formed, it was not clear if it would be Turing-complete\nor not.  The author observed it was possible to translate many programs\nwritten in [Loose Circular Brainfuck][] into Wagon, using the following\ncorrespondence:\n\n    +        iisiss\n    -        is\n    \u003e        iisrir\n    \u003c        iriisr\n    x[y]z    y@Xz\n\nLoose Circular Brainfuck is Turing-complete, so if this correspondence\nwas total, Wagon would be shown Turing-complete too.  However, the correspondence\nis not total.\n\nIt's the `@` that's the problem.  We can construct a \"while\" loop\nwith contents, and with operations that happen before it,\nand with operations that happen after it.  And the contents can themselves\ncontain a nested \"while\" loop.  But we cannot place a second \"while\" loop\n*after* an already-given \"while\" loop.  That is, we cannot have more than\none \"while\" loop on the same nesting level.\n\nThis might be best illustrated with a depiction of the nested structure that\na Wagon program represents.\n\n    -\u003e Tests for functionality \"Depict Wagon Program\"\n\n    -\u003e Functionality \"Depict Wagon Program\" is implemented by\n    -\u003e shell command \"bin/wagon depict %(test-body-file)\"\n\n    p@ I I I SII SII\n    ===\u003e Push1 Push1 Sub Push1 Push1 Sub Push1 Push1 Push1 (while Pop)\n\n    is@I  is@I\n    ===\u003e Push1 (while Push1 (while Push1 Sub) Push1 Sub)\n\n    isis@I  @I\n    ===\u003e Push1 (while Push1 (while Push1 Sub Push1 Sub))\n\n    i@Dp\n    ===\u003e Dup (while Push1) Pop\n\n    i@Dp i@Dp\n    ===\u003e Dup (while Dup (while Push1) Pop Push1) Pop\n\nWhile it is possible to simulate a universal Turing machine with only a\nsingle top-level \"while\" loop and a series of \"if\" statements inside the\nbody of the \"while\" (see, for example, [Burro][]), it is not known to me\nif it is possible to simulate a universal Turing machine with only\nstrictly-singly-nested \"while\" loops as constructible in Wagon.\n\nAny inner \"while\" loop can be turned into an \"if\" as described above,\nbut a conventional \"if/else\" is not possible, because it would normally\nrequire two consecutive \"while\"s, one to check the condition, and one\nto check the inverse of the condition.  Similarly, it would not be\npossible to check if the finite control of a Turing machine is in one\nstate, or another state.  This would seem to be a fairly serious restriction.\n\nHowever, shortly after being announced in the `#esoteric` IRC channel, it was\n[shown by int-e](https://gist.github.com/int-e/e4ae1f40f8173d67860d8f8e45c433c0)\nthat it is possible to compile a Tag system into a Wagon program.\nSince Tag systems are Turing-complete, Wagon is as well.\n\nAs of this writing, it remains unclear if Wagon is able to simulate a\nTuring machine or Loose Circular Brainfuck program directly rather than\nvia a Tag system.\n\nHappy trails!  \nChris Pressey  \nLondon, UK  \nAugust 13th or maybe August 16th or maybe even September 4th, 2019\n\n[Loose Circular Brainfuck]: https://esolangs.org/wiki/Loose_Circular_Brainfuck_(LCBF)\n[Carriage]: https://catseye.tc/node/Carriage\n[Equipage]: https://catseye.tc/node/Equipage\n[Burro]: https://catseye.tc/node/Burro\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatseye%2Fwagon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcatseye%2Fwagon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatseye%2Fwagon/lists"}