Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

<> 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


So parens are just a quoted list? Seems a little unnecessary but I wasn’t alive so I guess I don’t know.


> 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.


In 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.)


I don't agree with your assessment; parens are not giving a list literal since (1 <+ 2 3> 4) has been reduced to (1 5 4). Calculation cannot take place in a literal, by definition.

What this is doing is binding the parentheses to a list constructor. I.e. (1 2 3) is really something like <list 1 2 3>. It's more like a quasiliteral (like Lisp's backquote).

Unfortunately, the fact that the printed representation of a list is the same as the code which calculates it makes things somewhat "muddled", pun intended.

What if we have this: '(1 2 3 (4 5 6)). What is (4 5 6) here? It can't be the list (4 5 6), because (4 5 6) is code that constructs that list. Using the same printed rep for both is a very bad idea.


I meant list literal in the Python-like sense, so apologies for the confusion there.

In MDL, parens and angle brackets denote LISTs and FORMs, respectively. These are both objects with the same PRIMTYPE (a linked list) but are tagged differently. The evaluator evaluates these objects differently from each other: a LIST has each of its elements evaluated, with the results put in a new LIST. It truly is distinct from a FORM that has the same effect.

Distinctions like this were designed to make the corresponding parts of Lisp (like what you mentioned) less confusing.

Indeed, in your quote example, that (4 5 6) is a LIST, not a FORM, avoiding the very bad idea. Quasiquotation pretty much doesn't need to exist because of the ! sequence "operator" for splicing lists into lists and forms.


>Quasiquotation pretty much doesn't need to exist because of the ! sequence "operator" for splicing lists into lists and forms.

Hmm... I guess I don't know everything quasiquoting is used for in Lisp, but in MDL and ZIL, segments don't really solve the problem that quasiquoting does when it comes to writing macros.

That is, I usually want to use macros to generate code according to a template:

  <PRSI? ,FOO>
should become

  <==? ,PRSI ,FOO>
The way to write that macro is to call FORM to build the form:

  <DEFMAC PRSI? (X)
    <FORM ==? ',PRSI .X>>
But if it's a complex block of code, and the blanks I want to fill in are deeply nested, the template quickly becomes unreadable, because every structure turns into a form, and every form turns into a call to FORM.


Yeah, it does not completely replace quasiquotation, but in my experience the main things quasiquotation solves are (1) not having to write quote marks in front of every symbol and (2) not having to append lists yourself. MDL handles the first by making atoms self-evaluating (though the flip side is that you have to use , and . everywhere), and the second is handled by sequences. I agree that something like quasiquotation would make FORM construction in macros nicer to look at, though it is pretty nice that sequences work everywhere and not just inside quasiquotations.

As an example, consider the following possible implementation of PROG1 in terms of LET's implicit PROGN. They both evaluate a sequence of forms in order, but the first returns the value of the first form, and PROGN the last:

   (DEFMACRO PROG1 (form1 &REST forms)
      (LET ((temp (GENSYM)))
         `(LET ((,temp ,form1))
             ,@forms
             ,temp)))
A MDL equivalent would be something like (though I admit I'm entirely going off documentation!):

   <DEFMAC PROG1 (form1 "ARGS" forms "AUX" (temp (ATOM "temp")))
     <FORM PROG ((.temp .form1))
        !.forms
        .temp>>
Without quasiquotation, the Lisp example would be (without capitalization this time---in Lisp symbols are auto-capitalized):

   (defmacro prog1 (form1 &rest forms)
      (let ((temp (gensym)))
         (list* 'let (list (list temp form1))
            (append forms (list temp)))))
I chose this example to demonstrate how sequences alleviate the pain of splicing things into the middle of things.

Backtick is not used in MDL, right? And you're somewhat free to extend the MDL used in Zilf, right? You could get the best of both worlds by adding either a PREFORM type or an REBUILD form. For example:

   `< ... >  or   `< ... `>    ->   #PREFORM  ( ... )
or

   `struct   ->   <REBUILD struct>
The semantics would be that a PREFORM would be evaluated like a LIST, but then it would CHTYPE to FORM. Or, a REBUILD is an FSUBR that expects a structure, and it maps EVAL over the elements of that structure.

The macro would look like

   <DEFMAC PROG1 (form1 "ARGS" forms "AUX" (temp (ATOM "temp")))
     `<PROG ((.temp .form1))
        !.forms
        .temp>>
I don't know all the consequences of this change to MDL, though.


Interesting. I did find an implementation of backquoting in MDL, kind of:

https://github.com/PDP-10/muddle/blob/master/mim/development...

It doesn't run on any available MDL implementation, because it's actually for MIM, an even more obscure and undocumented extension of MDL. ZILF implements some parts of MIM, but doesn't implement READ-TABLEs at all, and the format of the table implied by that code is different from what's documented for MDL.


This is described in glorious high-definition ASCII art in Appendix 1 of The MDL Programming Language:

https://github.com/taradinoc/mdl-docs/blob/master/docs/appen...




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: