{"id":20425319,"url":"https://github.com/catseye/emmental","last_synced_at":"2025-08-08T02:49:08.665Z","repository":{"id":3601189,"uuid":"4665617","full_name":"catseye/Emmental","owner":"catseye","description":"MIRROR of https://codeberg.org/catseye/Emmental : A language based on meta-circular interpreters, precursor to Mascarpone.","archived":false,"fork":false,"pushed_at":"2023-10-25T12:36:52.000Z","size":69,"stargazers_count":9,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-26T13:11:52.722Z","etag":null,"topics":["esolang","esoteric-language","esoteric-programming-language","interpreter","meta-circular"],"latest_commit_sha":null,"homepage":"https://catseye.tc/node/Emmental","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-06-14T16:28:13.000Z","updated_at":"2024-04-11T21:52:19.000Z","dependencies_parsed_at":"2023-01-11T16:32:52.182Z","dependency_job_id":null,"html_url":"https://github.com/catseye/Emmental","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FEmmental","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FEmmental/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FEmmental/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catseye%2FEmmental/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/catseye","download_url":"https://codeload.github.com/catseye/Emmental/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","interpreter","meta-circular"],"created_at":"2024-11-15T07:12:52.245Z","updated_at":"2025-04-12T18:55:18.361Z","avatar_url":"https://github.com/catseye.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"The Emmental Programming Language\r\n=================================\r\n\r\n_Try it online_ [@ catseye.tc](https://catseye.tc/installation/Emmental)\r\n| _Wiki entry_ [@ esolangs.org](https://esolangs.org/wiki/Emmental)\r\n| _See also:_ [Mascarpone](https://codeberg.org/catseye/Mascarpone#the-mascarpone-programming-language)\r\n\r\n- - - -\r\n\r\nIntroduction\r\n------------\r\n\r\nEmmental is a self-modifying programming language. That is not to say\r\nthat it is a language in which programs are self-modifying; rather, it is\r\nthe language itself, as defined by a meta-circular interpreter, that can\r\nbe modified during the course of a running program. Indeed, this is how\r\nEmmental, without conventional conditional and repetition/recursion\r\nconstructs, achieves Turing-completeness.\r\n\r\nMeta-circular Interpreters\r\n--------------------------\r\n\r\nOne way to attempt to define a language is by giving what's called a\r\n*meta-circular interpreter* (often shortened to \"MCI\" in this document.)\r\nThis is an interpreter for some language which is written in that same\r\nlanguage (or at least, a language which is very close to it.)\r\n\r\nMeta-circular interpreters were a popular way to the describe the\r\nsemantics of programming languages, especially LISP-like languages, and\r\nespecially before the advent of denotational semantics. The term\r\n\"meta-circular\" was apparently coined by John C. Reynolds in his paper\r\n\"Definitional Interpreters for Higher-Order Programming Languages\" (1972\r\nProceedings ACM National Conference.)\r\n\r\nOf course, in the real world, MCI's are not often used. They certainly\r\n*can* be used: if you have a working Scheme interpreter that came with\r\nyour computer system, there is nothing stopping you from writing another\r\nScheme interpreter in Scheme, and running your programs on your\r\ninterpreter (which is itself running on your system's interpreter.)\r\nHowever, this is quite a bit less efficient due to the duplication of\r\neffort. A somewhat more realistic case might be if your system came\r\nwith, say, a Scheme compiler. You might then feed your Scheme\r\ninterpreter (written in Scheme) through that to make a native Scheme\r\ninterpreter, and use that to interpret your programs. (In this setup,\r\nthe interpreter is usually described as \"self-hosting\" rather than\r\n\"meta-circular\".)\r\n\r\nBut, as should be obvious, you already need an implementation of Scheme\r\nfor your Scheme interpreter written in Scheme to be of much practical\r\nuse to you. If your meta-circular interpreter is all you have, you won't\r\nbe able to use it to run or understand Scheme programs. Because the MCI\r\nis defined in terms of itself, you'll need some other source of\r\n\"understanding how it works\" to make it complete. This understanding\r\nmight come from an implementation in some other programming language, or\r\na specification in some formal language, or a description in some\r\nnatural language, or simply from intuition — but it has to come from\r\nsomewhere.\r\n\r\nAssuming that we do have that external source of understanding, the\r\nmeta-circular interpreter can come in quite handy in codifying the\r\nsemantics of the language. And, in Emmental's case, *altering* those\r\nsemantics: Emmental's MCI supports operations which instruct Emmental's\r\nMCI to modify its behaviour.\r\n\r\nInterpreter Structure\r\n---------------------\r\n\r\nTo describe the structure of Emmental's MCI, we first examine the\r\ngeneral structure of interpreters. If you've ever written a virtual\r\nmachine executor in, say, C, you've noticed that it has the general form\r\n\r\n        pc = start;\r\n        while (!done) {\r\n            switch (instruction[pc]) {\r\n                case INSTR_ONE:\r\n                    /* implement semantics of INSTR_ONE */\r\n                    pc += advance(INSTR_ONE);\r\n                    break;\r\n                case INSTR_TWO:\r\n                    /* implement semantics of INSTR_TWO */\r\n                    pc += advance(INSTR_TWO);\r\n                    break;\r\n                /* ... */\r\n                case INSTR_HALT:\r\n                    done = 1;\r\n                    break;\r\n                default:\r\n                    perror(\"Invalid opcode\");\r\n            }\r\n        }\r\n\r\nNote that `advance()` is some function that computes how far the program\r\ncounter is advanced on that instruction. This value is typically +1 for\r\nmost instructions, but more or less than 1 (and dependent on the state\r\nof the program) for a handful of \"branch\" instructions. Note also that\r\n`advance()` would not typically be implemented in C as a function; I'm\r\njust showing it like this to emphasize the regular structure.\r\n\r\nFrom this we infer that the basic structure of an interpreter is a\r\n*dictionary* or *map* that associates program symbols with operations.\r\nThere is some extra housekeeping like the fetch-execute cycle that\r\nsurrounds this dictionary, but this can (hopefully) be handled mostly\r\nautomatically, freeing us to concentrate on *symbols* and *operations*.\r\n\r\nThe symbols could be taken from any finite alphabet, but in Emmental, to\r\nkeep things relatively simple, we'll just use the ASCII character set.\r\n(Actually, to be precise, this is the full 8-bit ASCII character set.\r\nNon-printable control characters are allowed, as are characters between\r\n128 and 255, and each has a distinct meaning. But their representations\r\nare not defined.)\r\n\r\nThe operations can be thought of, abstractly, as functions which\r\ntransform program states. Or they can be thought of, concretely, as\r\nsegments of code — mini-programs which implement these functions. In the\r\ncase of a meta-circular interpreter, these mini-programs would be\r\nwritten *in the language being interpreted*.\r\n\r\nTo extend this idea to a *self-modifying* meta-circular interpreter, the\r\noperations can be thought of as functions which transform both program\r\nstates *and* interpreter definitions. (Alternatively, the interpreter\r\ndefinition could be thought of as part of the program state, although I\r\nfeel that's a bit gorier a way to look at it, and I prefer the other\r\nview, at least for Emmental.)\r\n\r\nIn Emmental, most operations leave the interpreter definition unchanged.\r\nHowever, there is one operation which alters the interpreter definition,\r\nand it is this altered definition that is used to interpret the\r\nremainder of the program.\r\n\r\nEmmental Semantics (in Emmental)\r\n--------------------------------\r\n\r\nEmmental is essentially a stack-based language. (There's also a queue,\r\nbut it's not as central.) All operations implicitly get data from, and\r\nimplicitly deposit results back onto, a single stack. For\r\northogonality's sake, this stack may contain only ASCII symbols. (And\r\nnote that trying to pop an empty stack, or dequeue an empty queue, is an\r\nerror that aborts the program.)\r\n\r\nNote that because we've established that an interpreter (at least,\r\ninsofar as Emmental ever needs to know) is simply a map that takes\r\nsymbols to operations, and that operations in Emmental are defined\r\n(meta-circularly) as Emmental programs, we can use the following\r\nnotation to describe interpreters:\r\n\r\n    % → XYZ+*!\r\n    \u0026 → 'ap'ag'ag\r\n\r\nThat is, the symbol `%`, when encountered in an Emmental program,\r\nindicates an operation that is defined by the Emmental program `XYZ+*!`,\r\nand so forth.\r\n\r\nWhen a main Emmental program begins execution for the first time, it\r\nstarts with what's called the *initial Emmental interpreter*. (This\r\nfact, of course, doesn't apply to any further point of execution inside\r\nan Emmental program, or execution of operations defined in Emmental's\r\nMCI, since these would be considered subprograms of a sort. These cases\r\nuse whichever interpreter happens to be in force in that point in time.)\r\n\r\nThe inital Emmental interpreter is defined as follows:\r\n\r\n    a → a\r\n    b → b\r\n    c → c\r\n    ...\r\n\r\nThat is, for every symbol x in the ASCII set, x `→` x.\r\n\r\nDoesn't tell us a lot about Emmental's semantics, does it? No. Nothing\r\nat all, really. But remember what I said about needing an external\r\nsource of understanding, in order to actually get full value out of an\r\nMCI. And remember the purpose of Emmental's MCI: it is not there so much\r\nto help us understand Emmental, but to allow us to *change* Emmental,\r\nfrom inside an Emmental program.\r\n\r\nAnd, for all that our description of the initial Emmental interpreter is\r\nunhelpfully tautological, it is not incorrect: the semantics of `a` can\r\nin fact be thought of as being defined by an Emmental program that\r\nconsists of only one instruction, `a`. This happy state of affairs comes\r\nabout because Emmental is stack-based; the \"signature\" (the requirements\r\nfor the \"before\" and \"after\" stacks) of the symbol `a` is the same as\r\nthe signature of the program containing the single symbol `a`. No extra\r\nsyntax to specify arity and the like is necessary.\r\n\r\nAbove all, don't panic: we *will* describe what symbols like `a`\r\nactually mean in Emmental, we'll just need to do it in something besides\r\nEmmental. In fact, let's do that right now.\r\n\r\nEmmental Semantics (in English)\r\n-------------------------------\r\n\r\n### Primitive Arithmetic\r\n\r\n`#` pushes the symbol NUL (ASCII 0) onto the stack.\r\n\r\nThe symbols `0`, `1`, ... `9` pop a symbol from the stack, multiply its\r\nASCII value by 10 modulo 256, add the value 0, 1, ... 9 (respectively)\r\nto that value modulo 256, and push the resulting symbol back onto the\r\nstack.\r\n\r\nThe upshot of these 11 operations is that you can push arbitrary symbols\r\nonto the stack by spelling out their ASCII values in decimal. For\r\nexample, `#64` pushes a `@` onto the stack.\r\n\r\n`+` pops two symbols off the stack, adds together their ASCII values\r\nmodulo 256, and pushes the symbol with the resultant ASCII value back\r\nonto the stack.\r\n\r\n`-` pops two symbols off the stack, subtracts the ASCII value of the\r\nfirst popped from the ASCII value of the second popped modulo 256, and\r\npushes the symbol with the resultant ASCII value back onto the stack.\r\n\r\n`~` (\"log\") pops a symbol from the stack, computes the discrete base-2\r\nlogarithm of the ASCII value of that symbol, and pushes the symbol with\r\nthe resultant ASCII value back onto the stack. The discrete base-2\r\nlogarithm of a number is the floor or integer part of the base-2\r\nlogarithm of that number. Alternately, it is the number of the highest\r\nbit position (starting with the LSB = bit position 0) with a bit set\r\nwhen the number is viewed as binary. Because the base-2 logarithm of the\r\nnumber 0 itself is undefined, the number 0 is treated as 256 for this\r\noperation; its discrete base-2 logarithm is 8.\r\n\r\n### Stack and Queue Manipulation\r\n\r\n`^` (\"enqueue a copy\") pops a symbol off the stack, makes a copy of it,\r\npushes it back onto the stack, and enqueues the copy onto the queue.\r\n\r\n`v` (\"dequeue\") dequeues a symbol from the queue and pushes it onto the\r\nstack.\r\n\r\nUsing these operations in combination, one can form \"discard\",\r\n\"duplicate\", \"swap\", and other more advanced stack manipulation\r\noperations. For example, assuming an empty queue and more than two\r\nelements on the stack, \"swap\" can be accomplished with the code\r\n`^v^-+^^v^v^v-+^v-+^v-+vv`.\r\n\r\nDespite this fact, the operation `:` duplicates the top value of the\r\nstack. (Emmental is not an absolutely minimal language; note, for\r\ninstance, that it has all ten decimal digits as operations when these\r\ncould surely have been defined in terms of only one or two operations.\r\nThe reasons for a seperate `:` operation are given below in the section\r\non Computational Class.)\r\n\r\n### I/O\r\n\r\n`.` pops a symbol off the stack and sends it to the standard output as\r\nan ASCII symbol.\r\n\r\n`,` waits for an ASCII symbol to arrive on standard input, and pushes it\r\nonto the stack.\r\n\r\n### Interpreter Modification and Reflection\r\n\r\nFirst let's define what it means to *pop a string* off the stack.\r\nSymbols are popped off the stack until a `;` symbol is found on the\r\nstack. The symbols popped off are considered a string in the reverse\r\norder they were popped; i.e. the last symbol popped is the first symbol\r\nof the string. The `;` symbol is popped off the stack, but is not made\r\npart of the string; it is simply discarded.\r\n\r\nSince an Emmental program is a string, popping a program is the same as\r\npopping a string, just that the string is interpreted as a program.\r\n\r\n`!` (sometimes called \"supplant\") pops a symbol, which we call s, off\r\nthe stack. Then it pops a program t. It then inserts the association s\r\n`→` t into the interpreter definition. This overwrites whatever mapping\r\nof s might have been in the interpreter definition previously. This new\r\ninterpreter definition is used for all subsequent execution (until it is\r\nchanged again, of course.)\r\n\r\nNote that `!` does *early binding*. That is, the meaning of each symbol\r\nin this program t is the meaning of that symbol *at the time `!` is\r\nexecuted*. If some subsequent `!` operation later changes the meaning of\r\none of the symbols that occurs in t, the meaning of t is not changed.\r\nThe semantics of t are \"captured\" or \"frozen\". This implies, among other\r\nthings, that supplanting some symbol z with itself (a program consisting\r\nonly of the symbol z) is a no-op, because z's meaning, at the time that\r\n`!` is executed, is invariably z.\r\n\r\n`?` (sometimes called \"eval\") pops a symbol, which we call s, off the\r\nstack. It then executes that symbol (interprets it as an operation) with\r\nthe interpreter currently in effect.\r\n\r\nNote that `?` does *late binding*. That is, in contrast with `!`, `?`\r\nnever \"freezes\" the semantic definition of the thing that it is\r\nexecuting. This is true even when `?` occurs in a operation redefinition\r\n(i.e. the program that supplanted some symbol's semantics when an `!`\r\nwas executed.) This implies, among other things, that supplanting some\r\nsymbol z with the program that consists of instructions that push the\r\nASCII value of z onto the stack, followed by a `?` instruction, creates\r\na *cyclic meaning* for z. This is because the z that will be executed by\r\nthe `?` will always be the present z, that is, the z that is executing\r\nthe `?`.\r\n\r\nFor convenience's sake, `;` pushes the symbol `;` onto the stack.\r\n\r\nAll other symbols are no-ops.\r\n\r\nComputational Class\r\n-------------------\r\n\r\nI believe Emmental is Turing-complete with only the operations that have\r\nbeen given so far, but I haven't proved it yet. All the elements are\r\nthere, and although some of them are somewhat \"cramped\", they look\r\nviable.\r\n\r\nIf you want to try thinking about how you could write real programs\r\n(like a universal Turing-machine simulator) in Emmental, you might want\r\nto skip this section, since it contains \"spoilers\".\r\n\r\nRepetition can be accomplished by assigning a symbol a cyclic semantics,\r\nby use of a `?` within a `!`. For example, we can redefine the semantics\r\nof `0` to be `#48?`. This is simply a program that pushes the symbol `0`\r\nonto the stack and executes it with the current interpreter, and, since\r\n`0` has been redefined to mean `#48?` in the current interpreter, this\r\nwill loop forever. The entire program to do this to `0` and run the\r\ninfinite loop is:\r\n\r\n    ;#35#52#56#63#48!0\r\n\r\nThis technique can also be used to \"jump\" from one definition to\r\nanother, by using `?` to execute some *other* symbol at the end of a\r\ndefinition (that is, some symbol other than the symbol being defined.)\r\n\r\nConditionals are a little more restrictive. The trick to them is,\r\nstrangely, the discrete log operator `~` in combination with the eval\r\noperator `?`. Since `~` maps all symbols into a set of nine symbols, and\r\n`?` executes the symbol on the stack, `~?` will execute one of the\r\nsymbols from ASCII 0 (NUL) to ASCII 8 (BS). We can then, for instance,\r\ndefine NUL to do one thing, define SOH through BEL to do the same as\r\nNUL, and define BS to do some other thing; this essentially\r\ndistinguishes between 0 (which executes BS) and every other value (which\r\nexecutes NUL). Further, we can use this in conjunction with `-` to\r\ncompare two values for equality. So, for example, a program which inputs\r\na character, and outputs Y if the character is M and N otherwise, would\r\nbe:\r\n\r\n    #59#35#55#56#46#!;##1!;##2!;##3!;##4!;##5!;##6!;##7!#59#35#56#57#46#8!,#77-~?\r\n\r\nIn case NUL through BS are in use for some reason, we can always add 9\r\nto the result of the log (`~#9+?`) to map the answer onto HT through\r\nDC1. Or, of course, any of a great number of other arithmetical mappings\r\nof our choosing. The most severe constraint is that there be 9 available\r\nsymbols to act as \"destinations\" for our \"branch\". Even if we never\r\noverwrite one \"branch\" with another (and we can do that in Emmental!)\r\nand even if we allocate one extra symbol to be the \"launch point\" of the\r\nbranch, we still have room for 25 branches in the ASCII character set.\r\n\r\nSo these parts look good. If there's a problem, it's with the queue.\r\nSpecifically, the problem seems to be the need to know the present size\r\nof the queue in order to do stack housework like \"duplicate\" and the\r\nsubsequent need for \"duplicate\" to achieve \"discard.\" (Duplicate can be\r\ndefined as `^v`, but this only works when the queue is empty. Discard\r\ncan be defined as duplicate plus `-+`, but this only works when there\r\nare other elements below the element being discarded. [This last point\r\nis not generally a problem since we can push arbitrary values onto the\r\nstack before any program.])\r\n\r\nHowever, if it turns out that we need \"duplicate\" or \"discard\" in order\r\nto write routines that can handle a variable-sized queue — and that\r\nstrikes me as likely — then it looks like we have a severe problem.\r\n\r\nHere's one way I could try to deal with it. I could say that the queue\r\nis *local* to the operation being defined (or the main program.) Then\r\nyou could define `:` to be `^v`, and inside `:`'s definition, the queue\r\nwould always initially be empty, so the definition would work.\r\n\r\nBut... we need the queue to store our global data. For example, if we\r\nare going to simulate a Turing machine, we'd need to use the queue to\r\nstore the tape (perhaps \"doubled up\", with one symbol of each pair\r\ntelling us \"the next symbol is a simulated tape symbol\" or \"the next\r\nsymbol is some housekeeping value.\") We can't store the tape on just one\r\nstack. And, once you are looping in Emmental, you've left the \"main\r\nprogram\" forever; you're jumping from definition to definition, and each\r\nhas their own queue. At best, you'd need to \"dump\" the queue onto the\r\nstack each time you switched definitions, and even then you'd need a\r\nloop to do that, and to loop you need to switch definitions. It's a\r\nroyal mess.\r\n\r\nSo here's how I will deal with it. I will add a primitive duplicate\r\noperation, `:`, to Emmental. Proving that Emmental is Turing-complete is\r\nstill, then, a challenge, although a doable-seeming challenge. I will\r\nthen propose a more formidable challenge: prove that the language formed\r\nby removing the `:` operation from Emmental is Turing-complete.\r\n(Equivalently, prove that the set of Emmental programs that begin with\r\n`;#0#58!` is Turing-complete. The nice thing about Emmental is that you\r\ncan always shoot yourself in the foot — until you erase your pistol,\r\nthat is.)\r\n\r\nAnd if you *really* like a challenge, try proving that Emmental without\r\n`~` is Turing-complete. I don't think that it is, although it's possible\r\nfor it to compute parity, at least (input a symbol, output E if its\r\nASCII value is even, and O if it's odd. To accomplish this, multiply the\r\ninput's ASCII value by 128 by adding 127 copies of it to it; this is\r\nmodulo 256, so the only results can be 0 or 128. Define those operations\r\nto print out E and O respectively. But that's as far as I've gotten.)\r\n\r\nDiscussion\r\n----------\r\n\r\n### Design Decisions\r\n\r\nI would've liked to have given Emmental a `'` or `\"` instruction similar\r\nto Funge's \"quote\" and \"quote-mode\" instructions; instructions that\r\ntreat one or more of the following symbols in the program literally,\r\npushing them, as symbols, onto the stack, instead of executing them.\r\nHowever, such instructions are somewhat problematic, both theoretically\r\nand (for the approach I took implementing Emmental) practically. There\r\nare two ways of thinking about the problems that arise.\r\n\r\nOne is that the function which implements `'` is given access to the\r\nprogram text itself, and possibly the position within the program, and\r\nit uses these to extract the \"immediate mode\" symbol past the `'`. This\r\ninformation could be available because these pieces of information are\r\nconsidered extra arguments to the function, or because they are (gorily)\r\nconsidered part of the overall program state. Either way, this operation\r\nis given a lot of information to work with, and for consistency (since\r\nwe want to be all nice and neat and say that all operations have the\r\nsame signature so that our dictionary has a nice uniform type,) *all*\r\noperations have access to this information. This is almost too much\r\ninformation; that is, it is so much that operations don't really *need*\r\nthe dictionary. We could just say there is *one* operation, defined by a\r\nfunction, and that function is given the current symbol and has to\r\ndecide what it means through whatever means it likes.\r\n\r\nThis approach is very powerful, of course, but it's just not the style\r\nthat Emmental embodies. (In fact, the idea to view interpreters as\r\ndictionaries was one of the foundational design choices for Emmental, to\r\nthe point where I started constructing a \"little theory of interpreters\r\nas maps.\" It really wasn't exploited as much as I think it could have\r\nbeen. If an interpreter is a map of symbols to strings of symbols, it's\r\nmuch more tractable than an opaque function would be; you can define all\r\nsorts of operations on them, for example concatenating two interpreters\r\n(for all symbols s in interpreter a and interpreter b, c[s] `→` a[s]b[s]\r\n— that sort of thing,) computing union or intersection of interpreters,\r\nCartesian product, etc.)\r\n\r\nThe other way of looking at it is to say that there are in fact\r\n*multiple* meta-circular interpreters available inside Emmental, and\r\nsymbols like `'` switch temporarily to an alternate MCI. This alternate\r\nMCI interprets every symbol as \"push this symbol\", then reinstates the\r\nprevious MCI. I like this explication better than the one above — MCIs\r\nbegin to look a bit like continuations! — but to do it justice would\r\ntake some work. I envision a language where the program has fine control\r\nover which MCI is in effect, possibly by keeping a map from symbols to\r\nMCIs, or maybe even being able to push MCIs onto the stack. This is a\r\nwee bit much for Emmental too though, and perhaps I'll explore these\r\npossibilities in a future language.\r\n\r\n### Turing-completeness\r\n\r\nYou can make the argument that Emmental's way of being Turing-complete\r\nis really nothing new: when you redefine some symbol, you're really just\r\ndefining a new function, and when you use `?` to execute that symbol\r\nfrom within its own definition, you're just making a recursive function\r\ncall.\r\n\r\nWell, yes, you can make that argument. But it has to do with how you\r\nthink about \"what is a language\", I think. Does a Pascal program\r\nfragment which defines a procedure called `PrintFibonacci` represent\r\nanother programming language, one different from Pascal? You could\r\ncertainly say that it does — it's the language Pascal where the token\r\n`PrintFibonacci` has some meaning that it doesn't have in Pascal.\r\n\r\nIn that view, any language where you can define procedures, or\r\nfunctions, or standard libraries, or the like, is an extensible\r\nlanguage. But even languages where you *can't* define new procedures or\r\nfunctions is arguably an extensible language. Take some initial\r\nBrainfuck program fragment, for instance. After it executes, it leaves\r\nthe Brainfuck tape and while-stack in some state that depends on the\r\ninput. Any Brainfuck fragment that executes after that, will execute in\r\nthat environment, and that environment is arguably a version of the\r\nlanguage Brainfuck, suitably extended.\r\n\r\nYou don't normally think of it that way, I bet, but you *could* — and\r\nyou would need to, to some degree, to claim that Emmental is \"just\"\r\ndefining new functions. The reason you don't typically look at languages\r\nlike this (unless you are very strange) is because it's much more useful\r\nto divide the world into \"languages\" and \"programs.\" And Emmental *does*\r\nmake this division, it just makes it in a slightly different place than\r\nusual.\r\n\r\nAs far as I'm concerned, if I describe what Emmental does as modifying\r\nthe Emmental language via its MCI, and what Emmental actually does is\r\nconsistent with the idea of modifying the Emmental language via its MCI,\r\nthen what Emmental effectively does is modify the Emmental language via\r\nits MCI. And if it needs to do this in a certain way in order to\r\nsimulate a universal Turing machine, then that difference (however\r\nslight) sets it apart from systems where this simulation needs to be\r\ndone by defining recursive functions.\r\n\r\nImplementation\r\n--------------\r\n\r\n`emmental.hs` is a reference interpreter for Emmental written in\r\nHaskell. Run the function `emmental` on a string; you can also run\r\n`debug` on a string to view the state of the program (stack \u0026 queue)\r\nduring execution. (Note that `debug` is *not* able to show program\r\nstates that occur internal to an operation.)\r\n\r\nHappy interpreter-redefining!  \r\nChris Pressey  \r\nChicago, IL  \r\nNovember 11, 2007\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatseye%2Femmental","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcatseye%2Femmental","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatseye%2Femmental/lists"}