[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)