5 ms·
There's something magically expressive about S-expressions and processing them. For a linguistic project I was thinking about how to represent sentence syntax a
by adamddev1 4y ago
There's something magically expressive about S-expressions and processing them. For a linguistic project I was thinking about how to represent sentence syntax as data and then I came across Chomsky's phrase structure analysis[1] which parses spoken language exactly like S-expressions in LISP! It's even illustrated with parenthesis or syntax trees, just like the old SICP vids explain. The phrase structure even uses recursive definitions so processing and inflecting the content of sentences can be done with recursive functions etc, (I did it in TypeScript) like something following a design exercise out of HTDP/SICP. I think the inherent parallel between human language phrase structure and S-expressions is beautiful.
[1]: This book introduces the phrase structure in a shockingly LISP-like way.
https://www.amazon.com/Grammar-Frank-Palmer/dp/B000S5VSAS https://www.amazon.com/Grammar-Frank-Palmer/dp/B000S5VSAS
https://en.wikipedia.org/wiki/Phrase_structure_rules https://en.wikipedia.org/wiki/Phrase_structure_rules
- skissane 4y ago> There's something magically expressive about S-expressions and processing them. A downside I see with S-expressions – you often end up with something like (FOO BAR BAZ), where FOO identifies the type of thing, and BAR and BAZ are parameters/slots/etc – but you have to remember the (sometimes quite arbitrary) order that BAR and BAZ go in. Some Lisp family languages (such as Common Lisp, Clojure, Scheme with SRFI 88/89) support keywords (aka named parameters), so you can do something like (FOO :bar BAR :baz BAZ), which is considered equivalent to (FOO :baz BAZ :bar BAR). However, this is somewhat of a late development in the Lisp tradition, the status of its adoption is mixed, and (arguably) it is moving away (even if only by a little bit) from pure S-expressions. When people say "S-expressions", they normally aren't thinking of CLOS. I think JSON's model, in which lists and objects are independent first-class entities, with distinct syntax, has something to say for it. The downside of JSON objects, is their keys are strings, not symbols, and they don't have any kind of "class slot" (some people make up conventions here, for example a reserved property name such as "$class", but those conventions are not uniform or standardised). Oh, and we don't need all those commas. I think, if I was designing a LISP-ish language, I'd have a first-class syntax for objects which used different opening and closing characters – maybe (FOO bar baz) for a list, but [FOO :BAR bar :BAZ baz] for an object.
- srcreigh 4y agoThis is equivalent to an association list (aka alist) which is a technique which goes back to JAM. (define xs `(foo (bar ,1) (baz ,0))) (second (assoc 'bar (rest xs))) ;; 1 (define (prop xs k) (second (assoc k (rest xs)))) (prop xs 'bar) ;; 1
- skissane 4y agoAssociation lists have some obvious downsides – if they didn't, Common Lisp-style keywords would have never been invented. One is that keywords are simpler syntax – `(foo :bar 1 :baz 0) is easier on the eyes than its association list equivalent. Another is that their native implementation (linear search) can be a performance drain if you use them too heavily in an application (especially if you have too many keys). It is a data structure which optimises for insertion over retrieval, but most applications retrieve much more than they insert. The ability to "shadow" (have multiple bindings for the same key, with the first being used and the subsequent being ignored, but potentially becoming active again when the earlier binding is removed) is useful in some cases, but more often than not an unnecessary feature, more likely to induce bugs than be genuinely beneficial – "shadowing" by accident rather than intention. A sufficiently smart runtime could optimise around some of these issues (this looks an association list, I'll use some optimised data structure to store it, but fall back on a more traditional cons-cell based storage if you try to do with it things the optimised data structure doesn't support.) But, having an actually separate data type enables equivalent performance optimisations, without anywhere near as much "smartness" in the runtime (which can be difficult to understand and maintain.) Plus, that kind of "smartness" can lead to difficult to understand performance regressions (why does adding this new code suddenly make completely unrelated parts of the application a lot slower?)
- samatman 4y agoLua's tables with metatables is a particularly eloquent solution to this, because as you say, it's always clear when a pair is being shadowed vs. replaced.
- adamddev1 4y ago