{"id":20425325,"url":"https://github.com/catseye/mascarpone","last_synced_at":"2025-04-12T18:55:14.225Z","repository":{"id":2727608,"uuid":"3722667","full_name":"catseye/Mascarpone","owner":"catseye","description":"MIRROR of https://codeberg.org/catseye/Mascarpone : You are lost in a twisty maze of meta-circular interpreters, all alike.","archived":false,"fork":false,"pushed_at":"2023-10-25T12:36:39.000Z","size":64,"stargazers_count":21,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-26T13:11:52.622Z","etag":null,"topics":["interpreter","meta-circular","programming-language"],"latest_commit_sha":null,"homepage":"https://catseye.tc/node/Mascarpone","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}},"created_at":"2012-03-14T21:40:43.000Z","updated_at":"2023-10-25T12:00:48.000Z","dependencies_parsed_at":"2022-09-09T21:41:03.453Z","dependency_job_id":null,"html_url":"https://github.com/catseye/Mascarpone","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%2FMascarpone","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FMascarpone/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FMascarpone/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FMascarpone/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/catseye","download_url":"https://codeload.github.com/catseye/Mascarpone/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":["interpreter","meta-circular","programming-language"],"created_at":"2024-11-15T07:12:53.128Z","updated_at":"2025-04-12T18:55:14.201Z","avatar_url":"https://github.com/catseye.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"The Mascarpone Programming Language\n===================================\n\n_Try it online_ [@ catseye.tc](https://catseye.tc/installation/Mascarpone)\n| _Wiki entry_ [@ esolangs.org](https://esolangs.org/wiki/Mascarpone)\n| _See also:_ [Emmental](https://codeberg.org/catseye/Emmental#the-emmental-programming-language)\n\n- - - -\n\n*You are lost in a twisty maze of meta-circular interpreters, all\nalike.*\n\nIntroduction\n------------\n\nMascarpone is a self-modifying programming language in the style of\n[Emmental](http://catseye.tc/node/Emmental/). In fact it is a\nrationalization and further exploration of some of the basic ideas\nbehind Emmental. In Mascarpone, meta-circular interpreters are\n\"first-class objects\": they can be pushed onto the stack, have\noperations extracted from and installed into them, and can themselves be\nmeta-circularly extracted from the language environment (\"reified\") or\ninstalled into it (\"deified.\") New operations can be defined as strings\nof symbols, and these symbols are given meaning by an interpreter that\nis \"captured\" in the definition, similar to the way that lexical\nvariables are captured in closures in functional languages. An operation\nmay also access, and modify, the interpreter that invoked it.\n\nLike Emmental, Mascarpone relies on meta-circular\ninterpreter-modification to achieve Turing-completeness. Unlike\nEmmental, Mascarpone is purely symbolic; there are no arithmetic\ninstructions.\n\nStack\n-----\n\nLike Emmental, Mascarpone is a stack-based language. Unlike Emmental,\nMascarpone's stack may contain things other than symbols. A stack\nelement in Mascarpone may be a symbol, an operation, or an interpreter.\n\nStrings are popped off Mascarpone's stack slightly differently than\nEmmental's. A string begins with the symbol `]` on the stack; this is\npopped and discarded. Symbols are then successively popped and prepended\nto a growing string. As further `]`'s are encountered, they too are\nprepended to the string, but the nesting level is incremented for each\none as well. Whenever a `[` is encountered, it is prepended to the\nstring and the nesting level is decremented, unless it is zero, in which\ncase the `[` is discarded and the string is complete. The net effect of\nall this futzing around is that `[]` work as nestable quoting symbols.\n\nAlso unlike Emmental, Mascarpone does not have a queue.\n\nMeta-circular Interpreters\n--------------------------\n\nThe idea of an interpreter in Mascarpone is similar to that in Emmental.\nIn Mascarpone, an interpreter is a map that takes symbols to operations,\nand an operation is a sequence of symbols that is given meaning by some\ninterpreter.\n\nOf course, this is a circular definition, but that doesn't seem\nunreasonable, since we're working with meta-circular interpreters. If\nyou like, you can think of it as forming an \"infinite tower of\nmeta-circular interpreters,\" but that's never been a really satisfying\nexplanation for me. As I explained in the Emmental documentation, I\nthink you need some source of understanding external to the definition\nin order to make complete sense of a meta-circular interpreter. (I also\nhappen to think that humans have some sort of innate understanding of\ninterpretation — that is, language — so that this demand for further\nunderstanding doesn't recurse forever.)\n\nThere is a special interpreter in Mascarpone called \"null\". It is an\nerror to try to interpret anything with this interpreter. Expect that\nany program that tries to do this will come crashing to a halt, or will\nspin off into space and never be heard from again, or something equally\nimpressive.\n\nEvery interpreter (except for null) is linked to a \"parent\" interpreter\n(which may be null.) No interpreter can be its own ancestor; the\nparent-child relationships between interpreters form a directed, acyclic\ngraph (or DAG.)\n\nThere is, at any given time in a Mascarpone, a current interpreter: this\nis the interpreter that is in force, that is being used to interpret\nsymbols. The parent interpreter of the current interpreter is generally\nthe interpreter that was used to execute the current operation (that is,\nthe operation currently being interpreted; it consists of a string of\nsymbols is interpreted by the current interpreter.)\n\nThe current interpreter when any top-level Mascarpone program begins is\nthe initial Mascarpone interpreter, which is described in English in the\nnext section.\n\nInitial Mascarpone Interpreter\n------------------------------\n\n`v` (\"reify\") pushes the current interpreter onto the stack.\n\n`^` (\"deify\") pops an interpreter from the stack and installs it as the\ncurrent interpreter.\n\n`\u003e` (\"extract\") pops a symbol from the stack, then pops an interpreter.\nIt pushes onto the stack the operation associated with that symbol in\nthat interpreter.\n\n`\u003c` (\"install\") pops a symbol from the stack, then an operation, then an\ninterpreter. It pushes onto the stack a new interpreter which is the\nsame as the given interpreter, except that in it, the given symbol is\nassociated with the given operation.\n\n`{` (\"get parent\") pops an interpreter from the stack and pushes it's\nparent interpreter onto the stack.\n\n`}` (\"set parent\") pops an interpreter i from the stack, then pops an\ninterpreter j. It pushes a new interpreter which is the same as i,\nexcept that it's parent interpreter is j.\n\n`*` (\"create\") pops an interpreter from the stack, then a string. It\ncreates a new operation defined by how that interpreter would interpret\nthat string of symbols, and pushes that operation onto the stack.\n\n`@` (\"expand\") pops an operation from the stack and pushes a program\nstring, then pushes an interpreter, such that the semantics of running\nthe program string with the interpreter is identical to the semantics of\nexecuting the operation. (Note that the program need not be the one that\nthe operation was defined with, only *equivalent* to it, under the given\ninterpreter; this allows one to sensibly expand \"intrinsic\" operations\nlike those in the initial Mascarpone interpreter.)\n\n`!` (\"perform\") pops an operation from the stack and executes it.\n\n`0` (\"null\") pushes the null interpreter onto the stack.\n\n`1` (\"uniform\") pops an operation from the stack and pushes back an\ninterpreter where all symbols are associated with that operation.\n\n`[` (\"deepquote\") pushes a `[` symbol onto the stack and enters \"nested\nquote mode\", which is really another interpreter. In nested quote mode,\neach symbol is interpreted as an operation which pushes that symbol onto\nthe stack. In addition, the symbols `[` and `]` have special additional\nmeaning: they nest. When a `]` matching the first `[` is encountered,\nnested quote mode ends, returning to the interpreter previously in\neffect.\n\n`'` (\"quotesym\") switches to \"single-symbol quote mode\", which is really\nyet another interpreter. In single-symbol quote mode, each symbol is\ninterpreted as an operation which pushes that symbol onto the stack,\nthen immediately ends single-symbol quote mode, returning to the\ninterpreter previously in effect.\n\n`.` pops a symbol off the stack and sends it to the standard output.\n\n`,` waits for a symbol to arrive on standard input, and pushes it onto\nthe stack.\n\n`:` duplicates the top element of the stack.\n\n`$` pops the top element of the stack and discards it.\n\n`/` swaps to the top two elements of the stack.\n\nDiscussion\n----------\n\n### Design decisions\n\nAs you can see, Mascarpone's semantics and initial operations are a lot\nless \"fugly\" than Emmental's. It's a more expressive language, in that\nit's easier to elegantly convey things involving interpreters and\nmeta-circularity in Mascarpone than it is in Emmental. It explores at\nleast one idea that I explicitly mentioned in the Emmental documentation\nthat I'd like to explore, namely, having multiple meta-circular\ninterpreters and being able to switch between them (and lo and behold,\nMascarpone has very well-developed `[]` and `'` operations.) It's also\n\"prettier\" in that there's more attention paid to providing duals of\noperations (both `*` and `@`, for example.)\n\nMascarpone also appears to be Turing-complete, despite the lack of\nexplicit conditional, repetition, and arithmetic operators. A cyclic\nmeaning can be expressed by an operation which examines its own\ndefinition from the parent interpreter of the current interpreter and\nre-uses it. A conditional can be formed by creating a new interpreter in\nwhich one symbol, say `S`, maps to an operation which does something,\nand in which all other symbols do something else; executing a symbol in\nthis interpreter is tantamount to testing if that symbol is `S`.\n\n\"But\", you point out, \"Mascarpone only has one stack! You need at least\ntwo stacks in order to simulate a Turing machine's tape.\" Actually,\nMascarpone *does* have another, less obvious stack: each interpreter has\na parent interpreter. By getting the current interpreter, modifying it,\nsetting it's parent to be the current interpreter, and setting it as the\ncurrent interpreter (in Mascarpone: `v`...`v}^`), we \"push\" something\nonto it; by getting the current interpreter, getting its parent, and\nsetting that as the current interpreter (`v{^`), we \"pop\".\n\nActually, even if there was no explicit parent-child relationship\nbetween interpreters, we'd still be able to store a stack of\ninterpreters, because each operation in an interpreter has its own\ninterpreter that gives meaning to the symbols in that operation, and\n*that* interpreter can contain operations that can contain interpreters,\netc., etc., ad infinitum. This isn't a very classy way to do it, but\nit's very reminiscent of how structures can be built in the lambda\ncalculus by trapping abstractions in other abstractions.\n\nIt's also worth noting that this is how you'd have to accomplish\narithmetic, with something like Church numerals done with interpreters\nand operations, since Mascarpone has nothing but symbols. On the plus\nside, this means Mascarpone, unlike Emmental, is highly independent of\ncharacter set or encoding — it doesn't even have to be ordered. Any set\nof symbols that contains the symbols of the initial Mascarpone\ninterpreter, plus the symbols appearing in the Mascarpone program being\nexecuted, plus the symbols that are desired for input and output, ought\nto suffice.\n\nActually, that's not quite true: it should be a *finite* set. This is\nmainly for the sake of the definition of the `'` operator: it switches\nto an interpreter where all symbols indicate operations that push that\nsymbol on the stack. From this we can infer that there should either be\na finite number of such operations (and thus symbols,) or somehow these\noperations know what symbol they are to push. They take the symbol that\ninvoked them as an argument, perhaps. But other operations in Mascarpone\ndo not have such capabilities: an operation need not even be invoked by\na symbol, as it could be invoked by the `!` operation, for instance.\nThat would make the operations in the `'` interpreter gratuitously\nspecial. And, practically, most character sets, on which sets of symbols\nare based, are finite, so I don't suppose this restriction is much of a\nproblem.\n\nOne further, somewhat related design decision deserves mention. Any\nsymbol which is not defined in the initial interpreter is interpreted as\na no-op. It probably would have been nicer to treat it as an explicit\nerror-causing operation. This could be extended to looking, inside each\nputative definition, for symbols undefined in the desired interpreter\nwhen executing a `*` operation, and causing a (preferably intelligible)\nerror early in that case. Semantics like this would have helped me save\ntime in debugging one or two of the test case programs. However, while\nMascarpone is arguably supposed to be less hostile than Emmental when it\ncomes to being programmed in, it's certainly still not what you'd call a\nmainstream programming language, so while I'm somewhat irked by this\ndeficiency, I hardly consider it a show-stopper.\n\n### Related Work\n\nThere are definitely two related works that are worth mentioning: Brian\nCantwell Smith's Ph.D. thesis \"Procedural Reflection in Programming\nLanguages\" (MIT, 1982,) and Friedman and Wand's paper \"Reification:\nReflection without Metaphysics\" (ACM LISP conference, 1984.) (Forgive me\nfor not giving proper, perfectly-formatted, Turabianly-correct\nreferences to these two works, but frankly, this is the age of the\nInternet: if you're interested in either of these papers, and you can't\nfind them, there's something wrong with you! If, on the other hand, you\ndon't have *access* to them, perhaps there's something wrong with the\ninstitutions whose assumed goal is to increase the amount of human\nknowledge — but not, it seems, to widen its availability.)\n\nIt's hard to say how much influence Smith's 3-LISP language and Friedman\nand Wand's Brown language (introduced in the respective papers) have had\non Mascarpone: probably some, since I had read both of them (well, not\n*all* of Smith's monster! but enough of it to grasp the main ideas, I\nthink) and thought about what they were trying to convey. (What Brown\ncalls \"reflection\" I've called \"deification\" to give a sort of\nphonological dual to \"reification\". Also, the term \"reflection\" seems to\nhave taken on a more general meaning in computer science since the\n'80's, so I wanted to avoid its use here.) But that was a couple of\nyears previous, and the subject of meta-circular interpreters came up\nthis time from a different angle; Mascarpone came primarily from trying\nto \"un-knot\" the ideas behind Emmental, which itself came to be, quite\nindirectly, from thinking about issues raised by John Reynolds' original\nwork on meta-circularity.\n\nCertainly a huge difference that sets Mascarpone apart is that 3-LISP\nand Brown are caught up in the whole LISP/Scheme thing, so they just use\nS-expressions and functions to represent reified interpreter parts,\nwhich include environments and continuations. Mascarpone, on the other\nhand, reifies whole interpreters at once, as values which are complete\ninterpreters. Because interpreters contain operations which contain\ninterpreters (\"ad infinitum\", one might think,) this approach seems to\nhighlight the meta-circularity in a way that is particularly striking.\nIn addition, Mascarpone's \"applicative\" organization (like XY or Joy;\nthat is, like an idealized version of FORTH) lets it avoid some of the\nreferential issues like names and environments, and gives a nice direct\none-symbol-one-operation correspondence.\n\nBecause Mascarpone has interpreters as first-class values, it is never\nobliged to make the guts of the running interpreter explicit during\nreification — it just needs to make that interpreter available as a\nvalue. The contract of the `@` operation (which, by the way, was a\nsomewhat late add to the language design, fulfilling the desire for a\ndual to `*`) says you get a program and an interpreter with semantics\n*equivalent* to the operation you specify, but it doesn't say *how*\nthey're provided. You could successively perform `@` on an intrinsic\noperation (like, say, `@` itself) and get successively more explicit\ndefinitions, written in Mascarpone, of what `@` means. Each one could be\nthought of as descending (or ascending? does it matter?) a level in that\ninfinite tower dealie. Or, you might only get back a single, random\nsymbol, and an interpreter where all symbols have the semantics of `@`,\nwith no explanation whatsoever. This inbuilt ambiguity is, I think, the\nappropriate level of abstraction for such an operation (in a\nmeta-circular context, anyway;) saying that you always get back the\nprogram you defined the operation with seems overspecified (and unable\nto handle the case of intrinsics,) and saying that you always get back\nsomething opaque, like a function value, seems quite nonplussing in the\ncontext of an interpreter that can supposedly examine its own structure.\nIt's not clear to me that either 3-LISP or Brown addresses this point to\nthis degree.\n\nAnd of course, neither 3-LISP nor Brown tries to use reification and\ndeification as a means of achieving Turing-completeness in the absence\nof conventional conditional and repetition constructs.\n\nImplementation\n--------------\n\n`mascarpone.hs` is a reference interpreter for Mascarpone written in\nHaskell. Run the function `mascarpone` on a string, or `demo n` to run\none of the included test cases. `mascarpone.hs` also has a much nicer\ndebugging facility than `emmental.hs`; you can run `debug` on a string\nto view the state of the program (the current instruction, the rest of\nthe program, the stack, and the current interpreter) at each step of\nexecution. And you can run `test n` to debug the test cases. Lastly,\nthere is a `main` function that runs `mascarpone` on a string read from\na file named by the first argument, so a Haskell compiler can be used to\nbuild a stand-alone Mascarpone interpreter from this source code.\n\nEven happier interpreter-redefining!  \nChris Pressey  \nChicago, IL  \nDecember 8, 2007\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatseye%2Fmascarpone","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcatseye%2Fmascarpone","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatseye%2Fmascarpone/lists"}