5 ms·
It seems like MDL uses both greater/less than and parens. Is this analogous to Scheme using both brackets and parens? That is, just for readability?
by mruts 8y ago
It seems like MDL uses both greater/less than and parens. Is this analogous to Scheme using both brackets and parens? That is, just for readability?
- jerf 8y agoIt was the early 80s. Readability wouldn't be invented for about another 20 years or so. Actually kinda serious. You need to be able to spare the RAM to have the comments, whitespace, large variables, breaking things nicely into functions, etc. I've got modules I've documented where the documentation alone would fail to fit into, say, a Commodore 64's RAM, as plain text. Early 1980s "big iron" would still be considered on the high end of embedded programming today. (In the sense that a Raspberry Pi is embedded programming.)
- blattimwind 8y agoThat's a really interesting perspective. If I think about it an application really doesn't need to be large or complex at all to accrue a couple hundred kB of source code, libraries not included.
- abecedarius 8y agoThey developed these on a https://en.wikipedia.org/wiki/PDP-10 https://en.wikipedia.org/wiki/PDP-10. It's not like they were editing/compiling on the C64. Of course targeting small systems does influence the coding style, but...
- jerf 8y agoA top-end PDP, according to that page, could address ~8MB, though I'm sure they didn't have it for a while; other charts on the internet suggest a top-end PDP-10 would get up to 33 mega Hz. 8MB for an embedded controller is no great ask and you have to crawl a ways down the spec sheet pile to get a 33MHz processor nowadays. A top-end PDP-10 would today put it roughly on par with an Arduino [1], although the Arduino would be a bit short on RAM. Even if you have the space to spare on comments and long variable names, you've been raised in a culture that considers those insanely expensive extravagances. [1]: https://www.arduino.cc/en/Products/Compare https://www.arduino.cc/en/Products/Compare
- abecedarius 8y agoHere's some code from the 1970s MIT Lisp/PDP-10 culture: https://github.com/dhess/rabbit-scheme/blob/master/rabbit.lsp https://github.com/dhess/rabbit-scheme/blob/master/rabbit.ls... The indentation and all-caps are not modern, and the MACLISP primitive names like TERPRI are unchanged from the 60s. Otherwise, the coding and commenting style would not really raise any eyebrows today. I first learned of literate programming from this culture instead of Knuth, e.g. https://dspace.mit.edu/handle/1721.1/41983 https://dspace.mit.edu/handle/1721.1/41983 ("The fourth section presents a completely annotated interpreter for AMORD"). I'm just saying yeah, expectations have definitely evolved, but it's not the case that PDP-10 hackers back then had not yet discovered or valued readability.
- jerf 8y agoYes, of course some people had that opinion. It was a fringe position back then, though. It's less fringe today, but I'm still not sure "code should be optimized for reading, not for writing" isn't still a fringe position, at least as evidenced by what people's actions say about their beliefs, rather than their words. Lip service of that is probably at least not fringe any more, which is progress of its own sort.
- abecedarius 8y agoAll I'm saying is that the Infocom Imps developed on a computer that was plenty big enough for comments and readable names to be no extravagance. The sources above show it happened in practice and not just in theory. The company started after they'd been used to this kind of environment for years. (And they were at least adjacent to actual literate-programming pioneers.) I'm not saying they were into literate programming, I'm saying it's misleading here to talk about what coding on the C64 was like.
- tyingq 8y agoCOBOL is pretty readable.
- LanceH 8y agodeceptively readable
- jmull 8y agoI first learned programming in BASIC on an 8K PET. The 8K was shared by the source code, data, the executing program and display memory. Spaces were for suckers. Each line had a few bytes overhead so you put as many statements on each line as you could. You stuck to 1 character for your common variables (only 2 characters were significant anyway). You used the abbreviated name for commands. (This was around 81, 82.) Something like this: > 10 FORT=1TO10:?"IHN!":NEXTT:FORT=1TO1000:NEXTT:GOTO10" This is from memory, so probably not actually correct code but this is meant to print "IHN!" 10 times, wait about a second and then repeat. T is used as a FOR loop variable twice.
- kps 8y ago<> is a FORM, which is evaluated. () is a LIST, which is not. <+ 1 2> ↦ 3 '<+ 1 2> ↦ <+ 1 2> (+ 1 2) ↦ (+ 1 2) <TYPE <+ 1 2>> ↦ FIX <TYPE '<+ 1 2>> ↦ FORM <TYPE (+ 1 2)> ↦ LIST
- mruts 8y agoSo parens are just a quoted list? Seems a little unnecessary but I wasn’t alive so I guess I don’t know.
- lisper 8y ago> So parens are just a quoted list? Seems a little unnecessary It's a very useful distinction. In regular Lisp, lists are the only data type that is not self-evaluating. This leads to confusion because the serialization of a list can mean different things depending on context. Consider: (eval (add 1 2)) (eval '(add 1 2)) Those forms both produce the same result when evaluated. It happens by two very different execution paths, but this is not immediately apparent when you run this code. So a beginner may well be surprised to learn that the following: (eval x) (eval 'x) may or may not produce the same result. So it's genuinely helpful to have two types of lists, one of which is self-evaluating, and the other which isn't, and to have syntax that distinguishes the two. Another example: all of these produce the same result: (eval (add 1 2)) (eval (eval (add 1 2))) (eval (eval (eval (add 1 2)))) and so on. But this is true only because (+ 1 2) evaluates to 3 which is self-evaluating. Contrast that with, say: (eval (cons 1 2)) which immediately produces an error. Trying to figure out why "add" "works" and cons doesn't and how to "fix" this can be very frustrating for a beginner.
- kmill 8y agoIn MDL, (1 <+ 2 3> 4) -> (1 5 4) '(1 <+ 2 3> 4) -> (1 <+ 2 3> 4) '<1 <+ 2 3> 4) -> <1 <+ 2 3> 4> <1 <+ 2 3> 4> -> an error (probably that 5 is not structured) Parens give list literals like in most modern languages. An interesting thing about MDL is that both FORM and LIST have the same "PRIMTYPE" so all the usual lisp-like list manipulations apply to both. A strange thing is the way these are implemented: pretty much every object (with fixed-size allocation) has a NEXT pointer, and a FORM or a LIST is an object that has a pointer (possibly null) to the first object. Taking the tail of a list entails allocating a new LIST object that points to the second element. You may wonder: how can an element be part of two lists at the same time? Well, you copy the object since it's in a 36-bit word. (Objects that do not fit in such a word are put in lists using a special DEFER object that points to it.)
- kmill 8y agoAngle brackets set out FORMs, parens set out LISTs. In English: FORMs are to function calls as LISTs are to list literals. A translation from MDL to Python would look like this: <a b c> -> a(b, c) (a b c) -> [a, b, c] (This is in contrast to a sibling comment: in MDL (<+ 1 2> <+ 3 4>) would evaluate to (3 7) ) However, there is a bit of a caveat in the translation. In MDL, atoms are self-evaluating, and it takes a call to LVAL or GVAL to obtain their stored values (as a shorthand, dot and comma prefixes give LVAL and GVAL calls, respectively). I haven't been able to find out from exactly which scope the evaluator gets the definition of a function. Which of the following two corresponds to what the evaluator actually does? <a b c> -> <.a b c> <a b c> -> <,a b c>
- taradinoc 8y agoFunction definitions normally go in the GVAL, so <DEFINE FOO () ...> is equivalent to <SETG FOO <FUNCTION () ...>> But EVAL will fall back to the LVAL if the atom doesn't have a GVAL. In other words: <A B C> is equivalent to <APPLY <COND (<GASSIGNED? A> ,A) (<ASSIGNED? A> .A) (T <ERROR>)> B C> https://github.com/taradinoc/mdl-docs/blob/master/docs/03-built-in-functions.markdown https://github.com/taradinoc/mdl-docs/blob/master/docs/03-bu...
- kmill 8y agoThanks! (And thanks for having those docs up, they have been useful to me the past couple days.) Another thing I couldn't find is exactly what a trailer with an empty OBLIST means. Does it use <ROOT>? It shows up in TELL for the uses of RETURN!-, which probably are to use a non-overridden version of RETURN. (Or any idea what SETG20 is supposed to mean? It shows up in Bureaucracy.)
- taradinoc 8y agoYeah, if there's nothing after the final !- in an atom, it means the atom is in <ROOT>. (See section 15.5 in the manual.) I'm not sure what the purpose of things like <RETURN!-> and <ORB!-> is. It could have something to do with the way they implemented Z-machine opcodes as MDL atoms, i.e. <RETURN!-> forces it to use the MDL SUBR instead of the opcode? Maybe they moved the MDL atoms that collided with Z-machine opcodes to a separate OBLIST, and this was needed to access them from MDL code in the same file? I never figured that part out, so when ZILF is generating code, it uses the same OBLISTs it used while interpreting and just looks things up by their PNAMEs. SETG20 and DEFINE20 are used in "MDL-ZIL" files, where routines are defined with DEFINE instead of ROUTINE, global variables are created with SETG instead of GLOBAL, etc. Presumably that was a way to run the games in MDL during development to avoid recompiling them. SETG20 and DEFINE20 are aliases for the MDL versions of SETG and DEFINE.
- taradinoc 8y agoAngle brackets and parentheses are just two of the many flavors of brackets you'll find in MDL. First, you got your regular, "boring" brackets: <SETG X 0> ;FORM (NORD BERT) ;LIST [1 MONEY 2 SHOW] ;VECTOR "Bracket-ish" ;STRING { what goes here? } ;TEMPLATE Then you got your exciting brackets, where the first ! is mandatory but the second is optional: !<REST .FOO!> ;SEGMENT ![2 4 8 16 32 64!] ;UVECTOR Then you got your hashtag brackets, which pop up in the corner of the TV screen reminding you that there's another way to CHTYPE: #FORM (+ 1 2) ;"same as <+ 1 2>" And your hashtag voucher brackets, which are really just defective angle brackets you can exchange for a free hashtag: <> ;"evaluates to #FALSE ()" Finally, you got your bogus brackets, things that look like they could be a new kind of exciting brackets, but when you look closely they're just prefix operators that tend to appear before brackets: '(OSCAR WILDE) ;"same as <QUOTE (OSCAR WILDE)>" %<+ 1 2> ;"evaluate immediately" %%<CRLF> ;"evaluate and discard result" See also: https://github.com/taradinoc/mdl-docs/blob/master/docs/07-structured-objects.markdown https://github.com/taradinoc/mdl-docs/blob/master/docs/07-st...