Live data from Hacker News

They Called It LISP for a Reason: List Processing (2005)

gigamonkeys.com

71–80 of 125 posts

Re: They Called It LISP for a Reason: List Processing (2005)

#71

Earlier quoted context omitted.

In homoiconic languages like lisp I think you'd need more flexibility than vectors. What if you need to remove an item for example, do you need to shift all remaining items to keep order or mark the removed section with Xs so that the cursor jumps to the next data item. You need to be able to randomly access bits and pieces to form a network.

Forth is also homoiconic (arguably more so than lisp) and it can be thought of as being purely vectors.

Forth's homoiconicity seems to be disputed: https://wiki.c2.com/?HomoiconicityClassification

Re: They Called It LISP for a Reason: List Processing (2005)

#72

Earlier quoted context omitted.

That is true, but some sort of a runtime bordering on being an interpreter needs to exist doesn't it. Because my brain cannot comprehend how you can do metaprogramming with macros etc. with static code. My opinion was that the compiled lisps precompile as much as possible (calculations etc.) into assembly and leave bits they can't compile intact, that's what make them fast.

Macros just is code that transform a (list) data structure into (another different list), and then feeds (another different list) into the compiler (rather than the original (list) going into the compiler: how things happen without macros). You can see how these things happen in like, Chicken Scheme (R5RS Scheme to C compiler).

I want to look at it to understand. But my guess is that this happens at runtime, doesn't it. If this happens at runtime, this means runtime evaluates the macro, lisp function is generated, generated lisp function is compiled and used.

Re: They Called It LISP for a Reason: List Processing (2005)

#73

Earlier quoted context omitted.

Lisp lists aren't linked lists. Pointers to TWO pointers (first pointer is named car, and the 2nd pointer is named cdr). The realization that (cons) / Lisp-lists / car-and-cdr give is that you can represent arbitrary graphs (!!!) with car / cdr / cons, leading to a truly universal data-structure. Not necessarily an _efficient_ datastructure mind you, but a universal one. ------ So really, Lisp-lists are just the "try…

> Pointers to TWO pointers (first pointer is named car, and the 2nd pointer is named cdr). This is not true; only cdr is a pointer to a pointer. car is a pointer to a value. For example, if x is (1 2), then (car x) is a pointer to the value 1, while (cdr x) is a pointer to the list (2), so only the latter is "to a pointer". > convert your C code into a vector or a hash-map. nit: C has neither vectors nor hash-maps, n…

(car [(This example) of a list]) == (This example), which is a pointer.

Re: They Called It LISP for a Reason: List Processing (2005)

#74
post #51

Earlier quoted context omitted.

I think building everything out of conses held Lisp back. It was possible, and it was deemed to be elegant, so people went too far with it. YMMV since my adventures with Lisp were around twenty years ago, but reading code, you'd see somebody constructing a list with a bunch of nested lists inside it, or some other complicated structure with conses, and you'd have no idea what it was or how they intended to use it, an…

I haven't used Lisp in years, but isn't it very easy to just name the list members by defining a structure ... thus making your use/purpose of the list elements quite transparent?

In a lot of Lisp code, people don't bother to use that feature, and instead do a lot of (undocumented (sets of ((lists) that ) have some unknown) undefined structure)

I never wrote Lisp code professionally, mostly just as a hobby for myself. It was all just dirty cons/cdr/car code.

Given how generic lisp-lists are, they're really free form and could be anything. They are probably "too generic" for a lot of applications, much like XML or JSON is overkill.

There's something to be said about writing configuration as .ini files by Python's ConfigParser, for example. Or other simpler files (even .csv files) and working off of that.

But Lisp gives you that "power" to have a true hierarchy / language built into your lists. With that power, comes great confusion 3 months later when you forget the structure.

-------

A good, professional programmer probably should:

1. Start with the Lisp-lists as a prototype

2. Simplify down to a simpler structure, like vector, or dictionary/HashMap, if possible.

Re: They Called It LISP for a Reason: List Processing (2005)

#75

Earlier quoted context omitted.

Lisp can be ahead of time compiled, being dynamically typed doesn't impact that. It does impact what gets generated by the compiler. Compiled CL code is usually "generic", it has logic inside that helps it dispatch based on the dynamic types during the runtime but this is not interpretation (in the sense meant by TCL, Python, and others). You can also specify the types and a compiler can, optionally, make (or use) mo…

I know that lisp compilers compile compute heavy trivial functions directly to machine code. But how is the output of a program containing a lisp macro for example. Let's say define a lisp macro don't call it and generate its assembly. What is the machine code output? This is the part I'm speechless about. "The logic inside that helps dispatch based on the dynamic types at runtime" is the interpreter part IMHO. Plus…

> But how is the output of a program containing a lisp macro for example.

I think you have a fundamental confusion about when and where macros are applied. In a compiled CL implementation, macros are expanded prior to compilation. The code will be exactly the same as if there was no macro involved. If you want to see what that might look like take the function body of this simple function:

  (defun bar (filename)
    (with-open-file (in filename :element-type '(unsigned-byte 16))
      (let ((contents (make-array (file-length in) :element-type '(unsigned-byte 16))))
        (read-sequence contents in)
        contents)))
And run it through `macroexpand` (`with-open-file` is the macro I'm particularly interested in here):

  CL-USER> (macroexpand
             '(with-open-file (in filename :element-type '(unsigned-byte 16))
               (let ((contents (make-array (file-length in) :element-type '(unsigned-byte 16))))
                 (read-sequence contents in)
                 contents)))
  (LET ((IN (OPEN FILENAME :ELEMENT-TYPE '(UNSIGNED-BYTE 16))) (#:G659 T))
    (UNWIND-PROTECT
        (MULTIPLE-VALUE-PROG1
            (PROGN
             (LET ((CONTENTS
                    (MAKE-ARRAY (FILE-LENGTH IN) :ELEMENT-TYPE
                                '(UNSIGNED-BYTE 16))))
               (READ-SEQUENCE CONTENTS IN)
               CONTENTS))
          (SETQ #:G659 NIL))
      (WHEN IN (CLOSE IN :ABORT #:G659))))
Now take that output and put it in a new function, call it `baz`. Then disassemble both `bar` and `baz` (I've done this and included the output of just one because they have the same instructions):

EDIT: Snipped the excessively long code, here's a link:

https://topaz.github.io/paste/#XQAAAQCODwAAAAAAAAAQYOhAaDnr7... (still long, but at least HN will truncate it)

It doesn't really make sense to talk about disassembling a macro as a macro is not, itself, what gets compiled when it's applied, but rather its output gets compiled (in a compiled Lisp).

> "The logic inside that helps dispatch based on the dynamic types at runtime" is the interpreter part IMHO.

Then every compiled program that uses a tagged union is really an interpreted program (which is a statement you could make a strong argument for, perhaps). The code above is still machine code, though, it's not being interpreted. This is part of the distinction between an interpreter and a runtime. Dynamic dispatch can be applied to statically typed languages as well as dynamically typed languages.

> We need to generate an example of something we can't do in C, something which requires evaluation at runtime.

Strictly speaking, there is no such thing as something that can be done in Lisp that cannot be done in C. It'll just take a lot more effort in C to do some things like compiling a function on the fly, returning it, and then applying it later. You'd at least need to link in a compiler and feed it some structure which it can then compile into a function which gets returned, probably, as a function pointer.

Re: They Called It LISP for a Reason: List Processing (2005)

#76

Earlier quoted context omitted.

I haven't used Lisp in years, but isn't it very easy to just name the list members by defining a structure ... thus making your use/purpose of the list elements quite transparent?

In a lot of Lisp code, people don't bother to use that feature, and instead do a lot of (undocumented (sets of ((lists) that ) have some unknown) undefined structure) I never wrote Lisp code professionally, mostly just as a hobby for myself. It was all just dirty cons/cdr/car code. Given how generic lisp-lists are, they're really free form and could be anything. They are probably "too generic" for a lot of applicatio…

My gut feeling is that this hasn't been true for a long time, unless you're really implementing a small test. Even then, using CLOS is quicker than deciding that "CDDR" refers to attribute "X".

Modern CL is referred to as a multi-paradigm language, and I almost always start a project by starting with "defclass" and "defmethod" like in any OO language.

Re: They Called It LISP for a Reason: List Processing (2005)

#77

Earlier quoted context omitted.

Macros just is code that transform a (list) data structure into (another different list), and then feeds (another different list) into the compiler (rather than the original (list) going into the compiler: how things happen without macros). You can see how these things happen in like, Chicken Scheme (R5RS Scheme to C compiler).

I want to look at it to understand. But my guess is that this happens at runtime, doesn't it. If this happens at runtime, this means runtime evaluates the macro, lisp function is generated, generated lisp function is compiled and used.

> But my guess is that this happens at runtime, doesn't it

Macros would be evaluated at compile-time (or arguably _right before_ compile time).

Re: They Called It LISP for a Reason: List Processing (2005)

#78
post #51

The real elegance of Lisp is that malloc() has been renamed to (cons) and everyone feels smarter about it. I jest a little bit, but that's really the fundamental thing about list-processing. For most code, you don't really care about runtime, and you really just need "a data structure that probably can solve the problem", and the (cons) based list of car and cdr solves it. List processing itself is an elegant techniq…

I think building everything out of conses held Lisp back. It was possible, and it was deemed to be elegant, so people went too far with it. YMMV since my adventures with Lisp were around twenty years ago, but reading code, you'd see somebody constructing a list with a bunch of nested lists inside it, or some other complicated structure with conses, and you'd have no idea what it was or how they intended to use it, an…

In a modern Common Lisp program, using conses for everything is serious code smell. It reeks of a very obsolete Lisp style and it makes code incredibly difficult to maintain. Doesn't matter if you give good names to deeply-nested list accessors; it's still a bad practice.

That doesn't mean conses and lists should be prohibited, but any use of conses/lists instead of arrays, CLOS objects, hash tables, or structs needs to be justified. And a programmer's ignorance of those other data structures is not a good enough justification.

Re: They Called It LISP for a Reason: List Processing (2005)

#79
post #51

The real elegance of Lisp is that malloc() has been renamed to (cons) and everyone feels smarter about it. I jest a little bit, but that's really the fundamental thing about list-processing. For most code, you don't really care about runtime, and you really just need "a data structure that probably can solve the problem", and the (cons) based list of car and cdr solves it. List processing itself is an elegant techniq…

I think building everything out of conses held Lisp back. It was possible, and it was deemed to be elegant, so people went too far with it. YMMV since my adventures with Lisp were around twenty years ago, but reading code, you'd see somebody constructing a list with a bunch of nested lists inside it, or some other complicated structure with conses, and you'd have no idea what it was or how they intended to use it, an…

Wasn't this the Clojure approach? You don't need cons cells to have s-expressions and an elegant core set of operations and data structures.

Re: They Called It LISP for a Reason: List Processing (2005)

#80
post #51

The real elegance of Lisp is that malloc() has been renamed to (cons) and everyone feels smarter about it. I jest a little bit, but that's really the fundamental thing about list-processing. For most code, you don't really care about runtime, and you really just need "a data structure that probably can solve the problem", and the (cons) based list of car and cdr solves it. List processing itself is an elegant techniq…

I think building everything out of conses held Lisp back. It was possible, and it was deemed to be elegant, so people went too far with it. YMMV since my adventures with Lisp were around twenty years ago, but reading code, you'd see somebody constructing a list with a bunch of nested lists inside it, or some other complicated structure with conses, and you'd have no idea what it was or how they intended to use it, an…

I don't think you're alone in this sentiment. I found myself on the wikipedia article for cons and found this witty quip in the Usage in Conversation section[0]:

> I sped up the code a bit by putting in side effects instead of having it cons ridiculously.

[0]: https://en.wikipedia.org/wiki/Cons#Use_in_conversation

Post reply on HN