4 ms·
Really nice code, as someone who isn't caught up entirely with the recent work that's gone into C++, it taught me some useful features! The taxonomy isn't quit
by madmax96 8y ago
Really nice code, as someone who isn't caught up entirely with the recent work that's gone into C++, it taught me some useful features!
The taxonomy isn't quite right for Lisp. It's actually simpler than the post. It should be something like this (very minimalistically):
<s-expression> ::= <atom> | ( <s-expression> . <s-expression> )
<atom> ::= <number> | <symbol> | <string>
...
The difference between this and what the author presents is that everything is an s-expression. s-expressions are either an atomic value or a pair of s-expressions. Convenient notation `(a b c ... z)` called "list builder notation" is provided so that we don't have to write `(a . (b . (c ... (Z . NIL))))`.
- KayEss 8y agoYeah, it's deliberately simplified in order to keep things short and sweet. Maybe I'll write some more posts that extend this into a real language and then these things will come out. I really wanted to show that core part of a programming language can be quite simple and nothing to be afraid of so everything else was subservient to getting that across :-)
- amelius 8y agoInstead of shoehorning everything into a generic "pair" structure, wouldn't it be much more powerful to build a separate type for everything?
- harperlee 8y agoWell the point is that you do not need to; it would just be an optimization opportunity.
- amelius 8y agoBut types have more uses than just optimization.
- harperlee 8y agoThat is completely correct. In lisp, the fact that syntax is so flexible enables the addition of a lot of functionality on top of the base language. Types and related functionality (checks, enforcement, conversion, etc.) is one of those things; but if everything is executed on top of pairs, there is a performance penalty that you may avoid if you provide more efficient data structures from the onset with a not-so-minimal implementation (such as clojure providing vectors, where you dont search a list in linear time). My “just” above meant that if you can do it slowly in the upper abstraction level, providing it in the lower one is basically an optimization. In any case in second thought what I said is not completely true, as there are things you may need to do in compile time and the minimal implementation does not provide macros.
- madmax96 8y agoIn practice, that's done. For instance, in common lisp there are procedures like `MAKE-ARRAY` and `MAKE-HASH-TABLE`. The interesting thing is that these hash tables and arrays are considered to be atomic. Clojure generalizes the notion of pairs, which in some ways is more elegant (a collection of items being atomic isn't necessarily intuitive) and in some ways is less (now your language has to describe how this abstraction operates).
- sparkie 8y agoThe usefulness of having a common structure for which to build types is that you can break them down from code into smaller types, or combine them into larger types, using just the functions `cons`, `car` and `cdr`. This is particularly useful for working on tooling which deals with the programming language code itself. A pair is just the most basic kind of compound data type. A triplet is just an atom plus a pair, and a quadruplet is an atom plus a triplet, and so forth. For any data structure containing N items, it can be represented as a sequence of N-1 cells. It's possible to map this to custom data types. Say you wanted a type of `vec {x, y, z}`, then you can represent it as a list `(x (y (z . ())))` where you could define `get-x = car`, `get-y = cadr` and `get-z = caddr` to access the elements by name. Some lisps let you encapsulate the result such that only those functions can be used on the encapsulated list. Rather then being more powerful to build everything into a language, it actually makes it less powerful when you try to do everything rather than providing the foundations on which other types can be built. See also: "Growing a language" by Guy Steele.