5 ms·
We actually need to talk about lists. The single reason why Lisp uses parentheses is the list notation -> programs are actually lists, not just text. The same
by lispm 3y ago
We actually need to talk about lists. The single reason why Lisp uses parentheses is the list notation -> programs are actually lists, not just text.
The same code which runs
CL-USER 10 > (let* ((foo 10)
(bar (+ foo 32)))
(* foo bar))
420
becomes data, when quoted:
CL-USER 11 > (quote
(let* ((foo 10)
(bar (+ foo 32)))
(* foo bar)))
(LET* ((FOO 10) (BAR (+ FOO 32))) (* FOO BAR))
A typical Lisp function can then substitute all * symbols to + symbols:
CL-USER 12 > (subst '+ '* '(LET* ((FOO 10) (BAR (+ FOO 32))) (* FOO BAR)))
(LET* ((FOO 10) (BAR (+ FOO 32))) (+ FOO BAR))
EVAL can execute lists:
CL-USER 13 > (eval (subst '+ '* '(LET* ((FOO 10) (BAR (+ FOO 32))) (* FOO BAR))))
52
Thus the single main feature is that Lisp itself is an application of the List Processor. This feature then is user visible in read eval print loops, debuggers, inspectors, code formatters, source interpreters, code generators (-> macros), embedded languages, ... So Lisp developers loved it, because one would not need translators between user facing (infix) syntax and internal data structures (ASTs, ...). Thus early attempts for other syntax variants failed, because this duality between data and code has some kind of elegance. Something which for example impressed Alan Kay (of OOP / Smalltalk / Dynabook / ... fame), when he understood that something like a Lisp evaluator can be written in a few lines of Lisp code, processing lists, which is code. -> https://www.quora.com/What-did-Alan-Kay-mean-by-Lisp-is-the-greatest-single-programming-language-ever-designed/answer/Alan-Kay-11?share=1 https://www.quora.com/What-did-Alan-Kay-mean-by-Lisp-is-the-...
- deleted 3y ago[deleted]
- taeric 3y agoI had fun trying to explain this a few years back. https://taeric.github.io/CodeAsData.html https://taeric.github.io/CodeAsData.html I do have to confess I have rarely taken huge advantage of this fact. But the idea is pretty easy to help look at code.
- kqr 3y agoList is confusing terminology. It's a tree. Sure, any node of a Lisp tree is implemented as a list of branches, but fundamentally we are talking about trees, still.
- zelphirkalt 3y agoWould you like to call them "TREEPs"?
- lispm 3y agoForemost, it's just nested lists and not a tree. We are talking about lists and atoms. We are then talking about list operations, like getting list elements, appending lists, reversing lists, listing things, nesting lists and atoms, mapping lists, matching lists, destructuring of lists, ... This is a list: (munich frankfurt dresden) This is a list of lists ((münchen :state Bavaria) (frankfurt :state hessen ) (dresden :state sachsen)) If we look at the level of conses and atoms, then we see a binary tree and operations: car, cdr, cons, ... Lisp is there a low-level language: lists are made of simpler building blocks: two-item nodes and atoms. This is a tree: ((a . (10 . nil)) . ((b . (20 . nil)) . nil)) The dot is an infix syntax element for a cons cell. Which as a nested list is shorter written as ((a 10) (b 20))
- skybrian 3y agoIt’s a shallow understanding of the code if you don’t know the variable definitions and types. Good enough for some things, though.
- lispm 3y agoCorrect, that's where for example an interpreter walks the code and builds up the understanding. Similar a compiler, a code walker or other tools. The shallow view allows in Lisp to provide new/different views of code. That's good and bad. Many transformations of code don't need a deep understanding, many do. In Lisp I can write (infix 3 + 1 * sin (x)), because an infix macro can provide its own interpretation, by transforming the code. But then I need to parse the code and find out what the things are: variables, literals, operators, calls, ... For example when I want to substitute FOO with a call: CL-USER 34 > (LET* ((FOO 10) (BAR (+ FOO 32))) (symbol-macrolet ((foo (print 2))) (* foo bar foo bar foo ; 1-3 (flet ((foo (a) (* foo a))) ; 5 (foo (* foo bar)))))) ; 4 2 ; 1 2 ; 2 2 ; 3 2 ; 4 2 ; 5 2370816 Here the symbol-macrolet knows which FOO to expand, and which not. There are five expansions in the code, which makes 2 printed five times. You can see that it works in nested code, too : it knows that some FOO are function names and those will not be expanded. Whereas a MACROLET knows, where an operator needs to be expanded: CL-USER 38 > (LET* ((FOO 10) (BAR (+ FOO 32))) (macrolet ((foo (a) `(print ,a))) (* foo bar foo bar (foo bar) ; 1 (flet ((bar (a) (foo a))) ; 2 (bar (* foo bar)))))) 42 ; 1 420 ; 2 3111696000