{"id":20425269,"url":"https://github.com/catseye/oxcart","last_synced_at":"2025-03-05T04:44:58.759Z","repository":{"id":142239944,"uuid":"218013070","full_name":"catseye/Oxcart","owner":"catseye","description":"MIRROR of https://codeberg.org/catseye/Oxcart : A continuation-passing concatenative language","archived":false,"fork":false,"pushed_at":"2023-12-12T20:33:41.000Z","size":50,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-15T15:11:50.845Z","etag":null,"topics":["concatenative-programming-language","continuation-passing","continuation-passing-style","esolang","esoteric-programming-language","purely-concatenative"],"latest_commit_sha":null,"homepage":"https://catseye.tc/node/Oxcart","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-10-28T09:42:03.000Z","updated_at":"2023-10-25T08:31:26.000Z","dependencies_parsed_at":"2025-01-15T15:08:39.532Z","dependency_job_id":"62fa1cdd-6ac0-4a07-a659-b8e92513c09c","html_url":"https://github.com/catseye/Oxcart","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FOxcart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FOxcart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FOxcart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FOxcart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/catseye","download_url":"https://codeload.github.com/catseye/Oxcart/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","continuation-passing","continuation-passing-style","esolang","esoteric-programming-language","purely-concatenative"],"created_at":"2024-11-15T07:12:44.850Z","updated_at":"2025-03-05T04:44:58.743Z","avatar_url":"https://github.com/catseye.png","language":"Haskell","readme":"Oxcart\n======\n\n_Try it online_ [@ catseye.tc](https://catseye.tc/installation/Oxcart)\n| _Wiki entry_ [@ esolangs.org](https://esolangs.org/wiki/Oxcart)\n| _See also:_ [Carriage](https://codeberg.org/catseye/Carriage#carriage)\n∘ [Equipage](https://codeberg.org/catseye/Equipage#equipage)\n∘ [Wagon](https://codeberg.org/catseye/Wagon#wagon)\n\n- - - -\n\nWhile desigining [Wagon][] the topic of continuations briefly came up.\nI didn't, at the time, think that thinking in terms of continuations\nwould make designing Wagon any easier.  But I did remark that a\ncontinuation-passing concatenative language sounded like an interesting\nthing in its own right.\n\nAfter Wagon was finished, I began thinking about how one would make\ncontinuation-passing concatenative language, but I immediately hit a wall:\nhow do you compose two functions written in continuation-passing style?\n\nSo I sat down and worked it out.  Maybe you can do it, I thought, if you\nadopt a non-standard formulation of function composition.\n\nIf conventional function composition is defined as\n\n    (f ∘ g)(x) = g(f(x))\n\nthen, rather arbitrarily picking the symbol ⊛ to denote it, composition\nof CPS functions can be defined as\n\n    (f ⊛ g)(x, κ) = f(x, λs. g(s, κ))\n\nor alternately,\n\n    (f ⊛ g) = λ(x, κ). f(x, λs. g(s, κ))\n\nThe question that remains is whether this is a workable substitute\nfor conventional function composition in a concatenative language.\n\nThis question has two parts: whether it's algebraically valid,\nand whether it's useful for writing programs with.\n\nAlgebraic properties of ⊛\n-------------------------\n\nThe first part.  Functions form a monoid under composition;\nthere is an identity element (the identity function):\n\n    e(x) = x\n\nand this is an identity because\n\n    (e ∘ f)(x) = f(e(x)) = f(x)\n    (f ∘ e)(x) = e(f(x)) = f(x)\n\nand composition is associative:\n\n    ((f ∘ g) ∘ h) = (f ∘ (g ∘ h))\n\nbecause\n\n    ((f ∘ g) ∘ h) = (f ∘ (g ∘ h))\n    (g(f(x)) ∘ h) = (f ∘ (h(g(x)))\n    (h(g(f(x))) = (h(g(f(x))))\n\nCan we devise an identity CPS function?  I think it might be:\n\n    ι(x, κ) = κ(x)\n\nand this is an identity because\n\n    (ι ⊛ f)(x, κ) = ι(x, λs. f(s, κ)) = (λs. f(s, κ))(x) = f(x, κ)\n    (f ⊛ ι)(x, κ) = f(x, λs. ι(s, κ)) = f(x, λs. κ(s))) = f(x, κ)\n\nAnd is ⊕  associative?  Well, let's try expanding it:\n\n    ((f ⊛ g) ⊛ h)\n    \n    replace (f ⊛ g) with λ(x, κ). f(x, λs. g(s, κ)):\n\n    (λ(x, κ). f(x, λs. g(s, κ)) ⊛ h)\n\n    replace (N ⊛ h) with λ(x, j). N(x, λt. h(t, j))\n    where N = (λ(x, κ). f(x, λs. g(s, κ)))\n    to get\n\n    λ(x, j). (λ(x, κ). f(x, λs. g(s, κ)))(x, λt. h(t, j))\n\n    Now reduce (λ(x, κ). f(x, λs. g(s, κ)))(x, λt. h(t, j))\n    by replacing in the lambda body\n    x with x and\n    κ with λt. h(t, j)\n    to get\n    f(x, λs. g(s, λt. h(t, j)))\n\n    and the whole thing reads\n\n    λ(x, j). f(x, λs. g(s, λt. h(t, j)))\n\n    which looks reasonable.\n\nVersus:\n    \n    (f ⊛ (g ⊛ h))\n\n    replace (g ⊛ h) with λ(x, κ). g(x, λs. h(s, κ)):\n\n    (f ⊛ λ(x, κ). g(x, λs. h(s, κ)))\n\n    replace (f ⊛ N) with λ(x, j). f(x, λt. N(t, j))\n    where N = (λ(x, κ). g(x, λs. h(s, κ)))\n    to get\n    \n    λ(x, j). f(x, λt. (λ(x, κ). g(x, λs. h(s, κ)))(t, j))\n\n    Now reduce (λ(x, κ). g(x, λs. h(s, κ)))(t, j)\n    by replacing in the lambda body\n    x with t and\n    κ with j\n    to get\n    g(t, λs. h(s, j))\n\n    and the whole thing reads\n\n    λ(x, j). f(x, λt. g(t, λs. h(s, j)))\n\nYes!  It looks like it is!\n\nA concatenative language with ⊛\n-------------------------------\n\nNow the second part.  This requires us to actually try to define some\nkind of concatenative language around this formulation of composition,\nand see what kind of programs we can write in it.\n\nLike [Carriage][] and [Equipage][] and [Wagon][], this will be\na \"purely concatenative language\": the entire program is a single\nstring of sequentially concatenated symbols, and each symbol\nrepresents a function, and the functions are sequentially composed\nin the same manner the symbols are concatenated.  More to the point,\nyou don't get to name anything or to nest anything inside anything else.\n\nUnlike [Wagon][] we won't be concerned with expressing control\noutside of the program state.  Indeed, first-class continuations are\na way to reify control as data, so we'll happily make them part of\nthe data store.\n\nI'm sure we could get away with having a single stack for the store,\nlike most concatenative languages, but to make things easier (maybe)\nlet's deviate slightly.  A store, in Oxcart, is a tape of stacks.\nThat is, it's an unbounded array of stacks, plus an index into that\narray.  The index is initially 0 but can be changed; the stack that\nit points to is referred to as \"the current stack\", and most\noperations operate on the current stack.\n\nEach stack is strictly FIFO and initially empty, and each stack cell\ncan hold either an int or a continuation.  Ints are generally assumed\nto be 64 bits in this day and age, but it pays to be cautious.\n\n### Basic operations\n\n    -\u003e Tests for functionality \"Evaluate Oxcart Program\"\n\n    -\u003e Functionality \"Evaluate Oxcart Program\" is implemented by\n    -\u003e shell command \"bin/oxcart %(test-body-file)\"\n\nThe instruction `0` pushes a zero onto the current stack.\n\n    | 0\n    = \u003e 0:[0]\n\nWhitespace is a no-op.\n\n    |       \n    = \n\nThese demonstrate how Oxcart stores are represented on output by\nthe reference implementation: the current stack is indicated by `\u003e`,\nfollowed by its index, then `:`, then its contents, top-to-bottom.\nBut only stacks that are non-empty are output.\n\nThe instruction `^` (resp. `v`) pops a value from the current stack,\nincrements (resp. decrements) it, and pushes the result back onto the\ncurrent stack.\n\n    | 0^^^0vv\n    = \u003e 0:[-2,3]\n\nThe instruction `:` pops a value from the current stack and pushes\ntwo copies of the value back on the stack.\n\n    | 0^^^^^^^^:^\n    = \u003e 0:[9,8]\n\nThe instruction `$` pops a value from the current stack and discards\nit.\n\n    | 0^^^^^$\n    = \n\nThe instruction `\\\\` pops the top two values, swaps them, and pushes\nthem back on the stack.\n\n    | 0^^^^^^^^0^\\0^^\n    = \u003e 0:[2,8,1]\n\n### Navigating the stacks\n\nThe instruction `\u003c` (resp `\u003e`) moves one space left (resp. right)\non the tape, changing which stack is the current stack.\n\n    | 0^^^^\u003c0^^^^^^^^\u003c0^^^^^^^^^^\u003e\n    =  -2:[10]\n    = \u003e-1:[8]\n    =   0:[4]\n\nThe instruction `(` (resp `)`) pops a value off the current stack,\nmoves one space left (resp. right) on the tape, and pushes the value\nonto the new current stack.\n\n    | 0^^^^\u003c0^^^^^^^^(0^^^^^^^^^^)\n    =  -2:[8]\n    = \u003e-1:[10]\n    =   0:[4]\n\nThe instruction `'` (apostrophe) pops a first value off the stack, then\na second value.  It then sets the tape position to the first value, and\npushes the second value back on the (probably newly current) stack.\n\n    | \u003c0^^^0^^^^^0^'\n    =  -1:[3]\n    = \u003e 1:[5]\n\nYou can of course push a dummy value, then discard it after moving it,\nif all you want to do is change to a different stack.\n\n    | \u003c\u003c\u003c\u003c\u003c\u003c00'$ 0^\n    = \u003e 0:[1]\n\nThe instruction `Y` pops a first value off the stack, then a second\nvalue off the stack.\n\nIf the first value is non-zero, nothing else in particular happens\nand evaluation continues as usual.\n\n    | 0^^0^0^Y0^^^\n    = \u003e 0:[3,2]\n\nBut if the first value is zero, the second value is added to the\ntape position (negative values go left, positive values go right).\n\n    | 0^^0^0Y0^^^\n    =   0:[2]\n    = \u003e 1:[3]\n\n    | 0^^0v0Y0^^^\n    = \u003e-1:[3]\n    =   0:[2]\n\n### Operations involving continuations\n\nThe instruction `S` pushes the current continuation onto the stack.\nNote that continuations don't have a defined representation other\nthan `#k`.\n\n    | S\n    = \u003e 0:[#k]\n\nThe instruction `%` pops a first value off the stack, then a second\nvalue off the stack.\n\nIf the first value is zero, nothing happens and evaluation continues\nas usual.\n\n    | S0%\n    = \n\nBut if the first value is non-zero, it replaces the current continuation\nwith the second value, and continues with that continuation.\n\n    | 0^^^0S0^%\n    = \u003e 0:[3]\n\nIn the preceding example, when `%` is evaluated, the 1 pushed by the `0^`\njust before the `%`, and the continuation pushed by `S`, are popped off\nthe stack (leaving 0 and 3 on the stack.)  The 1 is judged to be non-zero,\nso the continuation pushed by `S` is continued.  That continuation\nrepresents the remainder of the program that consists of `0^%`.  So a\n1 is pushed onto the stack and `%` is evaluated again.  But this time\n`%` gets a 1 and a 0, which is not a continuation, so things continue\nas usual.  The result is only the initial 3 on the stack.\n\n### Infinite loop\n\nSo we want to write an infinite loop.  In high-level terms, we need to\nsave the current continuation in a value _k_.  (Note that when we continue\n_k_, we'll end up back at this point.)  Then we want to continue _k_.\n(Note that, since we end up back at that point noted previously, we never\nget to this point.)\n\nWe can write this in Oxcart as:\n\n    S:0^%\n\n(We don't write this as a Falderal test, because we want all our tests\nto terminate.  But it is provided as a discrete program in the `eg/`\ndirectory, if you want to run it.)\n\n### Controlled loop\n\nSo we want to write a loop that terminates.  Say we want to generate\nthe numbers from 10 down to 0.  In high-level terms, we set a value\n_n_ to 10, and save the current continuation as _k_.  Then we make\na copy of _n_ and decrement it to obtain _n'_.  Then we make a copy\nof _n'_ and test if it's zero.  If it is, we're done.  If not, we\ncontinue _k_.\n\nWe can write this in Oxcart as:\n\n*   move left\n*   push 10 on stack\n*   move right\n*   push current continuation on stack\n*   duplicate\n*   move left\n*   duplicate\n*   decrement\n*   duplicate\n*   transfer right\n*   continue conditionally\n\nOr, as an actual Oxcart program:\n\n    | \u003c0^^^^^^^^^^\u003eS:\u003c:v:)%\n    =  -1:[0,1,2,3,4,5,6,7,8,9,10]\n    = \u003e 0:[#k]\n\n### While loop?\n\nSo, while we've demonstrated it's possible to write a controlled loop,\nit is in fact a \"repeat\" (or \"do\") type loop, where the loop body is\nalways executed at least once.  What about a \"while\" type loop, where\nthe loop body might not be executed at all, if the loop condition isn't\ntrue when the loop starts?\n\nYou may have noticed that the \"current continuation\" is a very palpable\nconcept in Oxcart; using the infinite loop program to illustrate, it is\nalmost as if concatenating the program symbols results in a program\nstructured like this:\n\n    S→:→0→^→%→■\n\nwhere each → is a continuation, and ■ is HALT, and execution happens by\nexecuting one instruction, then just following the attached arrow to get\nto the next instruction to execute.  An instruction like `S` has the\neffect of pushing the arrow (and, virtually, everything that follows it)\nonto the stack, and an instruction like `%` also has an arrow attached\nto it, but that arrow is ignored; an arrow popped off the stack is\nfollowed instead.\n\nBut one implication of this is that an Oxcart program can't access\nany continuation it hasn't already \"seen\", i.e. any continuations that\nit might encounter down the line, in the future.  In more pedestrian\nterms, you can't denote a forward jump.\n\nAnd that means we can't write a \"while\" loop in the usual manner.\n\nBut perhaps we can write one in a slightly unconventional manner.\n\nThe idea is this: the body of the loop is executed at least once,\nbut it is executed in a context where it has no effect on anything\nwe care about.\n\nThis might not work, but let's try to work it out.\n\nSo we want to write a \"while\" loop.  Say we have an _n_ on the\nstack, and we want to loop _n_ times, and _n_ might be zero.\n\nIn high-level terms, we first move to a \"junk stack\" and\nplace a \"junk _n_\" on it.\n\nThen, we save the current continuation as _k_.\n\nWe test if _n_ is zero.  If it is, we switch to a junk stack.\n\nThen, assuming we're on the real stack, we make a copy of _n_ and\ndecrement it to obtain _n'_.  Then we make a copy of _n'_ and test\nif it's zero.  If it is, we're done.  If not, we continue _k_.\n\nBut, assuming we're on the junk stack, the above becomes:\nwe make a copy of junk _n_ and decrement it to obtain\njunk _n'_.  Then we make a copy of junk _n'_ and test\nif it's zero.  If it is, we're done.  If not, we continue _k_.\n\nThis suggests our initial junk _n_ should be 1.\n\nThe problem is that we want to switch back from the\njunk stack to the real stack if previously we were on\nthe junk stack.  (This is what preciptated adding the\n`'` instruction to the language.)\n\nCan we can write this in Oxcart?\n\n*   transfer left (to move n to the data stack, -1)\n*   move left (to junk stack, -2)\n*   push 1 on stack\n*   reset to the main stack\n*   push current continuation on stack\n*   duplicate\n*   move left\n*   duplicate\n*   pop and if value is zero move one stack to the left\n*   duplicate\n*   decrement\n*   duplicate\n*   transfer value to the main stack (this is the test value)\n*   continue conditionally\n*   pop and discard the original pushed continuation\n\nIs this it?  With n=5:\n\n    | 0^^^^^\n    | (\u003c0^00'$S:\u003c:0v\\Y:v:0'%$\n    =  -2:[1]\n    =  -1:[0,1,2,3,4,5]\n\nAnd with n=0:\n\n    | 0\n    | (\u003c0^00'$S:\u003c:0v\\Y:v:0'%$\n    =  -2:[0,1]\n    =  -1:[0]\n\nHooray!  I think we just built a while loop.  One might need one\njunk stack per while loop, but one would only have a finite and\nfixed number of while loops in any given program anyway.\n\nI have not shown that it is possible to nest while loops.  Offhand,\nit seems plausible.  It may be slightly complicated, in that the\ntop-level while loop must junk its first iteration only once, but\nthe inner while loop needs to junk its first iteration many times.\nSo there might need to be some code that resets the inner loop's\njunk stack to a safely junkable state.  But it definitely feels\nmore like it's doable, than like it's insurmountable.\n\n### Minimality of Oxcart\n\nOxcart is not a minimal language.  It defines operations that are\nredundant with other operations.\n\nOne could define a \"Core Oxcart\" that omits the following operations:\n\n    \u003c\u003e\\\\\n\nBecause `\u003c` and `\u003e` can be thought of as just shorthands for `0v0^Y`\nand `0^0^Y`.\n\nAnd `\\\\` can be implemented using `\u003c`, `(`, `)`, and `\u003e`, as follows.\n\n    | 0^0^^\n    = \u003e 0:[2,1]\n\n    | 0^0^^)\u003c(\u003e\u003e(\u003c)\n    = \u003e 0:[1,2]\n\nOr in fact you can build a \"rotate\" of arbitrary finite depth with\nthose operations.\n\nIt's possible Core Oxcart could omit other operations, too, if they\nturn out to be not critical for showing that it is Turing-complete.\nIn particular, the `'` operation is very powerful, rather repulsively\nso; it's the only operation that lets you address tape cells in an\nabsolute fashion.  You might be able to use it where you would\notherwise use `()`.  It would probably be nicer to replace it with\nsomething that also works relatively, like `\u003c`, `(`, `)`, `\u003e`, and\n`Y` do.\n\nBut the goal of Oxcart was not to make a \"nice esolang\", and in\nfact the whole \"tape of stacks\" thing was entirely a secondary\ndesign choice; the main goal was to show that a contiuation-passing\nconcatenative language was viable, and I think it achieved that\ngoal.  Making a CPS concatenative language which is also a\n\"nice esolang\" can be saved for future work.\n\nBumpy trails!  \nChris Pressey  \nLondon, UK  \nOctober 28th, 2019\n\n[Carriage]: https://catseye.tc/node/Carriage\n[Equipage]: https://catseye.tc/node/Equipage\n[Wagon]: https://catseye.tc/node/Wagon\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatseye%2Foxcart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcatseye%2Foxcart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatseye%2Foxcart/lists"}