[HN Gopher] My mental model of setf was wrong
       ___________________________________________________________________
        
       My mental model of setf was wrong
        
       Author : nemoniac
       Score  : 143 points
       Date   : 2024-07-28 15:09 UTC (1 days ago)
        
 (HTM) web link (simondobson.org)
 (TXT) w3m dump (simondobson.org)
        
       | QuesnayJr wrote:
       | I have sometimes wished that "locations" in Common Lisp were real
       | lvalues, rather than an illusion made possible by macros.
        
         | reikonomusha wrote:
         | CL-LOCATIVES [1] make these first-class values. It essentially
         | turns any setf-able place into a mutable pointer.
         | 
         | [1]: https://github.com/stylewarning/cl-locatives
        
           | quotemstr wrote:
           | You know, what's wild is that Lisp machines had _kernels_ and
           | _garbage collectors_ implemented in Lisp too, making heavy
           | use of locatives as raw pointers to implement basic system
           | functions like writing to IO devices and doing garbage
           | collection. It 's wild that we all think the primitive ->
           | managed stack (ASM, then C/Rust/C++/etc., then Java/C#/Python
           | on top) is somehow an intrinsic property of computation, but
           | there are multiple existence proofs (of which the lisp
           | machine is one) of whole computing stacks implemented in
           | managed code and working just fine.
           | 
           | Conventional wisdom: "No... no... no... you can't implement
           | an OS kernel in managed code" Lisp machine OS authors: "Hold
           | me beer. [Laughs in 1970s]."
        
             | amszmidt wrote:
             | Most Lisp systems, be it Lisp Machines, or modern Common
             | Lisp implementations do the same. The Lisp Machine didn't
             | really have a kernel though, unless you call the microcode
             | that implemented the Lisp Machine macrocode "engine". As
             | for I/O, that was just memory mapped .. you write/read to
             | some magic number.
             | 
             | What is wild was invisible pointers ...                   A
             | forwarding pointer specifies that a reference to the
             | location containing it should be redirected to another
             | memory location, just as in postal forwarding. These are
             | also called invisible pointers.
             | 
             | (Not a member of the "we wall think we can't implement
             | systems in managed code" club -- there are plenty of such
             | systems dating back to before Unix :-) )
        
               | quotemstr wrote:
               | Yep. That era was like the Ediacaran biota of operating
               | systems design -- tons of weird and wonderful forms we
               | struggle to even _classify_ in contemporary terms.
        
             | lmm wrote:
             | I mean yes up to a point, but how do you even maintain such
             | a thing? Like, if your garbage collector was written by a
             | bunch of really smart guys who made sure that it would
             | never allocate during collection by just knowing the
             | internals of your system really well, sure that's great,
             | but then how would you even know when you'd broken that
             | property? And the failure mode isn't great (you presumably
             | get a very occasional deadlock when you just happen to run
             | down exactly the wrong codepath during GC).
             | 
             | Most languages will let you bypass their layering if you
             | want to. But it's usually ultimately a bad idea.
        
         | killerstorm wrote:
         | But that would be a language with completely different
         | semantics.
         | 
         | CL still tries to preserve something from lambda calculus.
        
           | phoe-krk wrote:
           | No, why? Earlier Lisps had locatives while preserving their
           | semantics, and https://github.com/stylewarning/cl-locatives
           | emulates this behavior (albeit with a slight performance
           | hit).
           | 
           | What in Lisp exactly is similar to lambda calculus, though?
           | Lisp isn't even a pure language.
        
       | waynecochran wrote:
       | Kramer once asked George "do you ever yearn?" Well I yearn. Every
       | time I see a post on common lisp I yearn to have a job where I do
       | nothing but mathematics and realize solutions in common lisp.
       | Unfortunately I have to get out of bed now and deal with janky
       | code written by someone who can only think imperatively.
        
         | AeroNotix wrote:
         | I feel you. I've written quite a lot of Common Lisp over the
         | years and I've tried to show off its capabilities to co-
         | workers, often in comparison to the language used that that
         | job.
         | 
         | Often I just get "huh, that's neat, now let's get back to
         | mining the coalface".
        
           | neilv wrote:
           | I have a suspicion that most Lisp articles we see on HN
           | _reinforce_ the perception that this is something other
           | people shouldn 't take seriously, much less touch.
           | 
           | This particular post is better than most, and seems like
           | genuine interest, and targeted at people already in the fold,
           | which is fine. Though probably no one else is going see it
           | and think "I gotta get me some of that."
           | 
           | The intentional advocacy posts, on the other hand... I
           | usually don't see them appealing well to even the minority of
           | programmers who are amenable to a low-employability platform.
           | While they seemingly help to keep the platform low-
           | employability, by making a weak pitch in the moment that
           | someone was curious/bored enough to look at that link.
        
         | mgsouth wrote:
         | Then one day you _really_ wake up, and realize you do have that
         | job. You hop out of bed with a spring in your step, walk into
         | your office, sit down at your desk...
         | 
         | and start debugging a misbehaving compiler macro used
         | everywhere in a 1500-line multi-nested LOOP.
        
           | Capricorn2481 wrote:
           | As someone who will probably never get the time or chance to
           | work in CL, I wish I saw more comments like these just to get
           | less FOMO
        
             | nerdponx wrote:
             | "All the bad things about Common Lisp" would be a fun blog
             | post to write up one day.
        
         | dgfitz wrote:
         | Every time I see a lisp post like this my first thought is
         | "just use the right tool for the job" and lisp is exactly NOT
         | always the right tool for the job.
        
           | waynecochran wrote:
           | Ah... these damn pragmatic facts always ruin everything!
        
             | dgfitz wrote:
             | Wayne, I heart you.
        
           | medstrom wrote:
           | If you think that every time, it is never the right tool for
           | the job?
        
             | dgfitz wrote:
             | I mean, if you want to be obtuse. Sometimes a lisp is the
             | right tool, sometimes it isn't.
             | 
             | Claiming that "getting out of bed and programming in
             | anything other than a lisp makes life less worth living" is
             | at best as extremely immature and strange stance to take.
        
           | boothby wrote:
           | To be entirely pedantic: if the job is developing a correct
           | mental model for setf, lisp may be _exactly_ the right tool
           | for the job.
        
           | waynecochran wrote:
           | There is one use of Lisp that dominates all other languages:
           | rapid algorithm prototyping. In fact, HN was founded by Paul
           | Graham who knew this secret and exploited it well.
        
             | dgfitz wrote:
             | Oh, I'm well aware. Turns out HN isn't successful because
             | of lisp.
             | 
             | Edit: upon further reflection, this whole "lisp evangelism"
             | is pretty tired. It's not some magic bullet. Idgaf if paul
             | goddamn graham knows how to write code in lisp. That means
             | basically nothing, at all.
        
           | mst wrote:
           | libera #perl often comments that part of the zen of perl and
           | actually being good at it is knowing when to use something
           | else.
           | 
           | Sometimes people get offended when we give them a fully
           | working awk/sed one-liner that does the job better but, uh,
           | that's what we would have used ourselves!
           | 
           | (or "that's seriously computation heavy, use
           | C/Rust/Julia/etc. because much though we love the perl5 VM
           | for quick scripts and large-scale OO business logic apps this
           | is not going to work nicely and trying to force perl to do it
           | will be a rathole")
           | 
           | Though I do sort of miss my 'green threads via call/cc in
           | guile sitting atop an ancient but functional perl event loop'
           | system, it was ~20 years ago now and I'm not suggesting
           | anybody else would ever want to use it, but in some ways it
           | was still nicer than modern async/await style event loop
           | driving nonetheless.
           | 
           | (yes, I'm an outlier and should not be counted)
        
             | dgfitz wrote:
             | We may be in violent agreement. Handing someone a awk line
             | instead of a lisp binary is exactly what I meant.
        
         | boothby wrote:
         | I'm a mathematician working in a common lisp shop, and
         | sometimes, I really do feel that I'm living the dream. We're
         | presently hiring, with more of a focus on software engineering
         | than doodling on pure math, of course, but we'd be eager to
         | interview somebody with love for math and common lisp. Email's
         | in my profile.
        
           | quotemstr wrote:
           | How did you guys end up with CL?
        
             | vindarel wrote:
             | It looks like CL is all the rage for quantum computing (and
             | some AI companies).
             | 
             | https://github.com/CodyReichert/awesome-cl
             | 
             | Thinking about recent feedback:
             | 
             | - https://blog.funcall.org//lisp%20psychoacoustics/2024/05/
             | 01/... (https://news.ycombinator.com/item?id=40233736)
             | (2024) - https://news.ycombinator.com/item?id=33467269
             | (2022) "Common Lisp was a conscious decision because it
             | allows a small team to be incredibly productive, plus the
             | fact that it's a live image allows you to connect to it
             | over the internet and poke and prod the current state,
             | which has really allowed a much clearer understanding of
             | the data." - https://lisp-journey.gitlab.io/blog/lisp-
             | interview-kina/ (2020)
        
           | waynecochran wrote:
           | Very enticing. My work is largely in computer vision and gpu
           | programming. I feel far removed the rare air I once breathed
           | programming lisp.
        
           | nerdponx wrote:
           | Funny, the mathematician I know that works at a CL shop hates
           | it, and is eager to transition into data science in a Python
           | shop. From what he has told me, the Allegro IDE is unpleasant
           | and antiquated-feeling, and the code is hard to work with /
           | not of particularly high quality.
        
         | quotemstr wrote:
         | The best you can do is bring the _principles_ of lisp systems
         | to more familiar languages. Lots of languages have lexical
         | closures, macro systems, and introspection capabilities. Python
         | is, famously, an  "acceptable lisp":
         | https://news.ycombinator.com/item?id=1803815
         | 
         | (Is something like Numba not a spiritual Lisp macro?)
        
           | vindarel wrote:
           | true, Python is a slow and unstable Common Lisp with no
           | interactive debugger, no restarts, no "live image"
           | interactivity, no core images, no binaries, no macros, no
           | compile-time computing, less compile-time type warnings, less
           | functional constructs, and a GIL.
        
             | brazzy wrote:
             | But also less parentheses!
        
             | mst wrote:
             | perl has closures, block based scoping, custom keywords
             | (not exactly a macro system but you can achieve similarly
             | awesome crimes against syntax), heavy introspection, an
             | optional condition system, compile time computing, an
             | integrated interactive debugger core with multiple clients,
             | more functional constructs than python, OO modeled on AMOP,
             | and no GIL.
             | 
             | Slow, bizarre in places, no live image though I can at
             | least trivially replace function implementations and add
             | methods to running code, binary building sucks, and there
             | is of course a whole ass list of things that are uniquely
             | awful (assume that the longest list you've seen from
             | somebody who hates the language has 50% overlap with my
             | personal list and that my list is probably longer) but ...
             | it works. For me, at least.
             | 
             | I loved scheme; I keep picking at common lisp getting
             | slowly better at thinking in terms of the capabilities of a
             | system that's image based and deeply condition-system-ed,
             | and I hope to get there eventually :D
        
               | ramenbytes wrote:
               | Do you have this list typed up somewhere?
        
         | dang wrote:
         | setf is pretty imperative!
        
           | waynecochran wrote:
           | Yes ... yes it is.
        
           | baq wrote:
           | one hard part I had to get over to start understanding what
           | lisp actually is... it's an imperative language. c.f. scheme
           | or clojure.
        
             | dang wrote:
             | I think Common Lisp is nicely cosmopolitan. It has great
             | support for whichever paradigm you want to program in,
             | and/or you can build your own.
        
               | ramenbytes wrote:
               | Like the Burger King of programming languages.
        
       | scoot wrote:
       | I misread the title as "My mental model of self was wrong", and
       | was expecting a very different article!
        
         | xanderlewis wrote:
         | I only realised it _didn't_ say that after reading this
         | comment.
        
           | tome wrote:
           | Me too. I skimmed through the comments assuming they were
           | talking about the Lisp equivalent of Python's self.
        
       | taeric wrote:
       | I don't know why setf always felt hard for me to really grok back
       | when I was starting programming. It has grown to feel extremely
       | natural. Is nice to know that for most "accessor" style
       | functions, you can use those in setf to update their value.
       | 
       | I never looked too hard at how this was accomplished. Fun
       | article!
        
         | martinflack wrote:
         | > It has grown to feel extremely natural.
         | 
         | That's how all of Common Lisp worked out for me. It all feels
         | strange, then after a couple years of hobbyist usage, it all
         | feels natural.
        
           | taeric wrote:
           | I wish I knew how to communicate that change to folks. There
           | is a lot of history on many of the forms that exist in common
           | lisp. And much of it is far more practical in origin than I
           | expected going into it.
        
       | vindarel wrote:
       | nitpicking, but I am not a fan of the naming. No need of a
       | capital "A" and no need to recall the class name in the accessor
       | names: "var" instead of "a-var" is enough, since yes, we can very
       | well have an accessor "var" for a class "b". "var" is a generic
       | function. So:                   (defclass a ()           ((var
       | :accessor var)))              (var *some-a-object*)  ;; => ok
       | (defclass b ()           ((var :accessor var)))              (var
       | *some-b-object*)  ;; => ok
       | 
       | > If we don't like using car to indicate the head of a list
       | 
       | ... then we can use `first` (and second, third... up to 9nth).
       | 
       | ---
       | 
       | https://lispcookbook.github.io/cl-cookbook/editor-support.ht...
       | 
       | https://github.com/CodyReichert/awesome-cl
        
       | phoe-krk wrote:
       | _> But the names setf synthesises aren't recognised by defun and
       | defgeneric_
       | 
       | Oh, they are. (SETF FOO) is a valid function name in Lisp - even
       | though it's a list, not a symbol.                   CL-USER>
       | (defun (setf foo) (newval) (format t ";; Setting ~S~%" newval)
       | newval)         (SETF FOO)         CL-USER> (setf (foo) 42)
       | ;; Setting 42         42
        
       | guhcampos wrote:
       | Somehow I read this as "my mental model of self was wrong" and it
       | totally resonated with me.
       | 
       | Then I saw it was about LISP, which I don't know/use and got a
       | little bummed out.
       | 
       | Now maybe my mental model of self is wrong and I need to learn
       | LISP and create a mental model for setf and that will lead me to
       | an awakening of a correct mental model of self?
        
         | monarchwadia wrote:
         | Am also similarly bummed :)
        
         | layer8 wrote:
         | Sounds like your mental model of the title was wrong. ;)
        
         | bryanrasmussen wrote:
         | read the same, I interpreted it as failing eyesight and
         | tiredness.
        
         | msla wrote:
         | I think you need to learn Smalltalk to have a mental model of
         | Self:
         | 
         | https://en.wikipedia.org/wiki/Self_%28programming_language%2...
        
           | myhf wrote:
           | this
        
         | cypherpunks01 wrote:
         | Same. I was reminded of a quote I like from a Mongolian
         | Buddhist lama:
         | 
         | "It's not that you're not real. We all think we're real, and
         | that's not wrong. You are real. But you think you're _really_
         | real -- you exaggerated. "
        
         | ramenbytes wrote:
         | > Now maybe my mental model of self is wrong and I need to
         | learn LISP and create a mental model for setf and that will
         | lead me to an awakening of a correct mental model of self?
         | 
         | Could lead to an awakening of the psychological reality of
         | Lisp:
         | https://web.archive.org/web/20180727081543/http://www.pgc.co...
        
       | killerstorm wrote:
       | > This is a method specialised twice: on the type of an argument
       | (A), and the selector used to within the locator (a-var).
       | 
       | This part is wrong: (setf a-var) is simply the name of the
       | method.
        
       | blacklion wrote:
       | Am I only who read title as "My mental model of _self_ was wrong
       | "? And thought it is about psychology, not about Self programming
       | language.
        
         | bombela wrote:
         | You are not alone.
        
           | jujube3 wrote:
           | And yet, you are alone. So very alone.
        
       | lispm wrote:
       | 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 wrote:
         | Wow that is a great description of awful design choices. The
         | purpose of SETF is avoiding learning the language?
        
           | chmod775 wrote:
           | Language 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 wrote:
           | The 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 wrote:
           | the 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 wrote:
           | You 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 wrote:
           | The 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.
        
           | pkhuong wrote:
           | The purpose of the place system is to support side-effectful
           | macros where programmers don't have to repeat what they mean
           | n times.
        
       | natrys wrote:
       | My Lisp education was mostly from Elisp, but this I understood
       | early when I was reading some random code and came across:
       | (setf (buffer-string) "hello")
       | 
       | I thought this was just bananas, turns out that Elisp setf simply
       | has a bunch of hardcoded rules for matching getters/setters for
       | even a lot of non-standard data structures like Buffer that are
       | Emacs specific. So the above gets rewritten to:
       | (insert (prog1 "hello" (erase-buffer)))
       | 
       | It's not exactly AGI but still very cool.
        
         | michaelhoffman wrote:
         | This is not "hard-coded" as I would describe it. It is defined
         | in `gv.el`:                   (gv-define-setter buffer-string
         | (store)       `(insert (prog1 ,store (erase-buffer))))
         | 
         | Unfortunately, it is marked obsolete since 29.1. The NEWS says:
         | 
         | * Many seldom-used generalized variables have been made
         | obsolete. Emacs has a number of rather obscure generalized
         | variables defined, that, for instance, allowed you to say
         | things like:                   (setf (point-min) 4)
         | 
         | These never caught on and have been made obsolete. The form
         | above, for instance, is the same as saying
         | (narrow-to-region 4 (point-max))
        
       | pfdietz wrote:
       | One interesting thing one can do with SETF is define expanders
       | that dive into accessors on "pure" datatypes, copying as needed.
       | 
       | Suppose we have a pure cons type, call it PCONS, with accessors
       | PCAR and PCDR. Then, the form
       | 
       | (setf (pcar (pcdr x)) y)
       | 
       | turns into (effectively; the actual expansion has temporary
       | variables to avoid duplicating forms)
       | 
       | (progn (setq x (pcons (pcar x) (pcons y (pcdr (pcdr x))))) y)
        
       ___________________________________________________________________
       (page generated 2024-07-29 23:00 UTC)