4 ms·
Lambdas are about delayed evaluation. Macros are about disabling evaluation. Lisp code is just an abstract syntax tree notation format (s-exps) that comes with
by j-pb 8y ago
Lambdas are about delayed evaluation.
Macros are about disabling evaluation.
Lisp code is just an abstract syntax tree notation format (s-exps) that comes with default evaluation semantics. Macros allow you to disable those evaluation semantics to reuse the AST for a different programming language.
So for example to add pattern matching facilities to the language, you come up with a syntax and its representation in s-exp AST and then write a macro that describes the unification operation using the default semantics.
Same for logic programming or any other paradigm not initially supportet.
Languages like clojure also bootstrap quite a bit of the language from a simpler to implement dialect. (look at all the functions with a * in clj source they're the base language)
- lispm 8y agolambdas are anonymous functions. Macros are code transformers. Lisp code is not an AST.
- j-pb 8y agoDifferent name for the same thing, whats your point? Lambdas/AF are used to delay evaluation but keep the default semantics. Macros are more than simple code transformers, that wording somehow implies that they somehow retain the semantics of the data passed to them, which mighy be the case but is not required at all. S-Expressions are just a serialisation format for the m-expression AST.
- lispm 8y agoLambdas are just functions. Delaying functionality is just one use of functions. > that wording somehow implies that they somehow retain the semantics of the data passed to them Since they can do arbitrary transformations, retaining semantics is not in focus. Since the input may not have any semantics defined, the semantics is actually provided via the macro implementation. > S-Expressions are just a serialisation format for the m-expression AST. S-expressions know nothing about 'syntax'. Thus they can't be an 'abstract syntax tree'. (3 4 +) is a valid s-expression, but carries no information about any syntax (what is the + ? in an s-expression it's just a symbol) and is also an invalid Lisp expression. An abstract syntax tree would be the result of parsing a program according to some grammar and it would represent the syntactic categories. The parsing stage would already eliminate invalid programs of that programming language. https://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/Abstract_syntax_tree_for_Euclidean_algorithm.svg/800px-Abstract_syntax_tree_for_Euclidean_algorithm.svg.png https://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/Ab... In Lisp it is important that the s-expression is NOT a syntax tree. Otherwise it would be difficult to write macros which violate Lisp syntax.
- agumonkey 8y agoI know you're rigorous and mostly right, but you forget to admit that for most practical uses s-exps encode trees and are used as ad-hoc AST's. People just make implicit grammars based on spec like predicate patterns.
- j-pb 8y agoHe's not even right. He's pseudo rigorous to support is CL zealot trolling. He made it far enough into the wikipedia article to find a graphic that pseudo supports his claim, but not far enough to actually read the definition of an Abstract Syntax Tree (the thing we talk about) vs Concrete Syntax Tree (the thing he talks about). > This distinguishes abstract syntax trees from concrete syntax trees, traditionally designated parse trees, which are typically built by a parser during the source code translation and compiling process.
- lispm 8y agoYou might want to reread the Wikipedia article and tone down a bit: Check out the abstract syntax tree on the right: https://en.wikipedia.org/wiki/Abstract_syntax_tree It has a node which is a BRANCH and which has three relations CONDITION, IF-BODY, ELSE-BODY. In an s-expression this is just (if (> a b) (setq a (- a b)) (setq b (- b a)) Thus there is no representation that IF is a branching expression, there is no representation that A and B are variables. There is no representation that > is a compare op. And so on. The s-expressions are just nested lists of tokens without any idea what the tokens refers to or what language construct it stands for. All we know is what the tokens are and a hierarchy. A is a symbol, but what kind we don't know: it could be a data object, a variable name, a function name, a goto tag, a name of a class, a name of a type, ...). In the abstract syntax tree the > is identified as a compare op, IF is identified as a branch, A is identified as a variable identifier, ... The Lisp reader also does not create that information. It just creates a data structure, which could be anything, any kind of data.
- j-pb 8y ago> Delaying functionality is just one use of functions. This is why explicitly said Lambdas. Name one use of lambdas not covered by functions that is not delayed evaluation. If you want to be pedantic please be so consistently. > S-expressions know nothing about 'syntax'. Thus they can't be an AST. You seem to be confusing what the CL Hyperspec calls syntax, which is actually the semantics of evaluating a certain expression list, with the actual parse tree that can be generated from M-Expressions. And you seem to confuse Abstract Syntax Trees which can contain pretty much anything with Concrete Syntax Trees generated by parsing (it's explained on the very wiki page you linked). And even IF they were the same, M-Exps themselves are so flexible that they can express everything as function calls and arguments, and since the original Lisp only had a single symbol type your (3 4 +) argument itself is flawed. You're way to focused on how CL does things, maybe it's your CL background that actually causes you to miss the bigger picture of lisp, something you accuse the people of that come from Java.
- chriswarbo 8y agoI thought I'd weigh in, as a casual Racket/Elisp user > Lambdas/AF are used to delay evaluation but keep the default semantics. You and lispm seem to agree that lambdas can be used to delay evaluation, e.g. `(lambda () (+ 1 2))`. They are also used to abstract/parameterise, e.g. `(lambda (x) (* x x))`. These two ideas coincide in most languages, since it's often not clear how we would evaluate a parameterised expression (e.g. how might we evaluate the `(* x x)` above, whilst keeping `x` as a free parameter?). Hence most languages don't evaluate inside a parameterised expression (lambdas) until called, and hence we can use them to delay evaluation. In fact, we can evaluate "under a lambda", which is what happens during inlining, constant folding, or more generally partial-evaluation and supercompilation. This shows that parameterising and delaying evaluation are two distinct ideas, but they're usually represented with one language construct (lambda). We could imagine a separate language construct, like `(delay foo)`, which prevents any form ofevaluation (including inlining, supercompilation, etc.) from being applied to its argument, until it's 'forced' (maybe with a 'force' construct, or maybe by overloading function call syntax). As for whether Lisp code is a syntax tree I would say: - If we're allowing the possibility of the reader being altered, then our code doesn't have to be a tree, but I personally wouldn't call it "Lisp code" if it differed that much from s-expressions, regardless of whether a Lisp implementation can read it or not. In that case it's more like we're repurposing a Lisp implementation to act on some non-Lisp language. For example, if we used reader macros to consume regular expressions, that doesn't mean that regular expressions are Lisp. - To me "Lisp code" is a syntax tree, but it is concrete not abstract (for the reasons lispm says, e.g. `+` is just an opaque symbol rather than an arithmetic operator; we can interpret it in any way we like, using macros). - I wouldn't say that Lisp code is s-expressions, since there are alternative representations (e.g. I-expressions, sweet-expressions, etc.). Yet as long as they're equivalent to s-expressions (which, in particular, requires that they're trees and that we can impose our own semantics using macros!), then I'm happy calling them Lisp.
- lispm 8y ago> You and lispm seem to agree that lambdas can be used to delay evaluation, e.g. `(lambda () (+ 1 2))` Right, and this is not different from any other function: (define (foo) (+ 1 2)) (define bar (list foo (lambda () (+ 1 2)))) ; putting two functions in a list for later evaluation ((first bar)) ((second bar)) > They are also used to abstract/parameterise, e.g. `(lambda (x) (* x x))`. yep.
- bluefox 8y agoNote that you may also have a Lisp form like #1=(programmable . #1#), which represents not a tree but a graph. With lispm's remarks in mind, you can see that it's not abstract, not syntax, and not a tree.
- lerax 8y agoAs always, lispm given a free lession...
- j-pb 8y agoYeah mentioning clojure in a thread where he's active is a surefire way to get downvoted. That guy hates everything not CL...
- lispm 8y agoExperience shows that most Clojure users come from Java and have very little idea about Lisp. It might help to bring in some perspective, since Clojure is not a very typical Lisp: no interpreter, no linked lists as base data structure, no Lisp in Lisp, no Lisp runtime, no images, not the typical debugging tools like break loops, the runtime is from the JVM or another environment (.net, JavaScript), ... Lisp existed already before Clojure and has a rich history, much of which is not in Clojure.
- dig1 8y ago> no interpreter ClojureScript is interpreter, unless you run Closure compiler in compilation phase. Interpretation is done by default in repl. There is also Joker [0]. > no linked lists as base data structure Not true. Linked lists are base data structure on the same level as vectors and hash maps. Unlike Scheme where hash maps are sometimes implemented as assoc-ed lists. > no Lisp in Lisp Check for CinC and derivatives [1] or Ferret [2]. > no Lisp runtime What you define by "runtime"? > no images By this requirement, 90% Lisps out there would not be Lisp ;) Even some Common Lisp implementations (ABCL) doesn't have it. Or if you want another extreme, V8/nodejs can generate image/snapshot or you can dump JVM image via Criu [3]. > not the typical debugging tools like break loops IntelliJ has it, CIDER has it, there is clj-debugger... It is not part of default implementation, just like Scheme doesn't have it. [0] https://joker-lang.org/ https://joker-lang.org/ [1] https://github.com/remleduff/CinC https://github.com/remleduff/CinC [2] https://ferret-lang.org/ https://ferret-lang.org/ [3] https://www.criu.org/Main_Page https://www.criu.org/Main_Page