5 ms·
My basic mental model of SETF was always relatively simple. We were told that in university, many decades ago. Lisp has traditionally a bunch of different data
by lispm 2y ago
My basic mental model of SETF was always relatively simple. We were told that in university, many decades ago.
Lisp has traditionally a bunch of different datastructures. Like cons cells with CAR and CDR accessors. RPLACA and RPLACD were the setters, from a time when characters were expensive, so that identifiers were short and abbreviated (RPLACA would be short for Replace CAR). Then there were property lists with accessors GET (-> Maclisp) or GETPROP (-> Interlisp). The setter was PUTPROP.
So for any datastructure we need to learn both a getter and the corresponding setter.
With SETF in Common Lisp we only need to know the getter. SETF knows for each getter a corresponding setter.
So for a datastructure FOO we don't need to know both GETBAR and BARSET!.
The getter is (GETBAR SOMEFOO) and the setter is (SETF (GETBAR SOMEFOO) SOMETHINGNEW). SETF then is a macro which knows how to set the thing.
Advantage: we don't have to learn ancient functions like RPLACA with calls like (RPLACA CELL 42), but we can write (SETF (CAR CELL) 42). Take the accessor and wrap a SETF around it, include the form for the new value.
In reality there is a bit more magic involved.
- jmount 2y agoWow that is a great description of awful design choices. The purpose of SETF is avoiding learning the language?
- chmod775 2y agoLanguage features are superseded by better ones all the time. Nobody gets language design correct on the first try, especially when requirements change over time and by application. If you look at the history of languages like JS or B/C/etc. you better strap in for a crazy ride.
- Zambyte 2y agoThe purpose is to avoid learning a large vocabulary in favor of a smaller, more general vocabulary. That is abstraction, and it's a feature of the language, so you can't learn it while avoiding learning the language.
- smegsicle 2y agothe purpose of setf is to provide an intuitive, powerful, extensible, and simple to use abstraction for an extremely common operation without introducing any new syntax
- kazinator 2y agoYou should still know about the rplacd function. You can map over it. E.g., delete all middle conses of an assoc list, gluing the first and last one together: [1]> (reduce #'rplacd (list (cons 'a 1))) (A . 1) [2]> (reduce #'rplacd (list (cons 'a 1) (cons 'b 2))) (A B . 2) [3]> (reduce #'rplacd (list (cons 'a 1) '(cons 'b 2) (cons 'c 3) (cons 'd 4))) (A D . 4) If someone finds an application for this, drop me a PM. :) New people learning Common Lisp don't need to know about rplacd, or not right away. Firstly, destructive manipulation of lists should be deemphasized; it is an advanced topic in itself which can produce bugs. People who advance to system level work in Lisp, like doing work on implementations, will have to know it. rplacd can appear in code that cannot use setf, because it is processed before setf has been boostrapped. You will run across rplacd in other people's code even if you don't use it yourself; so if you go beyond just writing programs for yourself into collaboration and maintenance of existing code, you can't avoid knowing about it. If you ever have to debug the code produced by (setf (cdr place) ...), you will likely see rplaca or a similar function.
- bsder 2y agoThe point is to make accessing a data structure somewhat "generic". I only have to change the "(car a)" to "(superdupercar a)" and the "setf" propagates the changes along. This is especially important inside macros. It is also the reason why Lisps can feel so icky. This is a huge hunk of "macro magic" that can be damn near impenetrable when something goes wrong. The longer I'm at this the more I'm convinced that macros are the reason why Lisp lost. Even if I concede that macros are hugely powerful, every language that leans into something equivalent to macros winds up with inscrutable errors, debugging issues and/or excessive compile times.
- kazinator 2y agoIf you change (car a) to (superdupercar a), then (setf (superdupercar a) x) no longer works. You have to program the generalized place system of Common Lisp to recognize (superduper ...) as a place, and define what that means; then all is well. It could be as simple as (defsetf superdupercar superduperrplaca) If you have a function (superduperrplaca a x).
- deleted 2y ago[deleted]
- pkhuong 2y agoThe purpose of the place system is to support side-effectful macros where programmers don't have to repeat what they mean n times.
- amno 2y agoYeah, that is great idea, and I agree it sounds great on paper. However, some time ago, I have come to a conclusion that it soon becomes quite verbose and tedious. I think it worked better in the past when people used acronyms and very short names for function and variable names. Today, with self-documenting code style, names are longer for both functions and names. Constantly typing full paths gets annoying quite soon. Code is also more verbose to read and less code fits into 80 columns. My personal conclusion is that I actually prefer to abstract those away with a proper name like aset, put, and so on, just to make my own code less verbose. If they really wanted to simplify the language, they should have perhaps removed 'setq' and 'setf' and just kept 'set' in the language but with the powers of setf. Not to mention that users have to learn how to write own setf accessors, unless it is very simple stuff (the extra bit of magic you mention). As a "simplification" of the language I think they have failed, if that was the goal. That does not mean that 'setf' is not useful. On the contrary, it is a very useful tool to be able to compute and set a "place", just shouldn't be sold as a simplification.