[HN Gopher] Why I Program in Lisp
       ___________________________________________________________________
        
       Why I Program in Lisp
        
       Author : ska80
       Score  : 220 points
       Date   : 2025-04-11 08:26 UTC (14 hours ago)
        
 (HTM) web link (funcall.blogspot.com)
 (TXT) w3m dump (funcall.blogspot.com)
        
       | DeathArrow wrote:
       | >It's less of a big deal these days, but properly working lambda
       | expressions were only available in Lisp until recently.
       | 
       | I think Haskell and ML had lambda expressions since like 1990.
        
         | imgabe wrote:
         | Recent, compared to Lisp
        
           | IsTom wrote:
           | Number of programmers that are in workforce that started
           | before Standard ML (1983) is tiny and this argument would be
           | relevant only to them.
        
             | mikelevins wrote:
             | The author of the referenced post is one of them, though.
        
         | User23 wrote:
         | The word "properly" is not only working hard here, but perhaps
         | pointing to deeper concepts.
         | 
         | In particular, it implies a coherent design around scope and
         | extent. And, much more indirectly, it points to time. EVAL-WHEN
         | has finally made a bit of a stir outside Lisp.
        
           | BalinKing wrote:
           | Does this imply that lambda expressions in Haskell and ML
           | _don 't_ have a "coherent design around scope and extent"?
           | This is quite a claim, to be honest....
        
       | damnitbuilds wrote:
       | "properly working lambda expressions were only available in Lisp
       | until recently."
       | 
       | until -> since
        
         | JadeNB wrote:
         | > "properly working lambda expressions were only available in
         | Lisp until recently."
         | 
         | > until -> since
         | 
         | I think "only since recently" is not standard English, but,
         | even if it were, I think it would change the intended meaning
         | to say that they were not available in Lisp until recently, the
         | opposite of what was intended. I find it clearer to move the
         | "only": "were available only in Lisp until recently."
        
           | mikelevins wrote:
           | It's perfectly good and idiomatic English, but it's an
           | ambiguous formation and your suggested edit does clarify it.
        
             | JadeNB wrote:
             | I agree that "properly working lambda expressions were only
             | available in Lisp until recently" is perfectly idiomatic,
             | but easily misunderstood, English. I believe that the
             | suggested fix "properly working lambda expressions were
             | only available in Lisp since recently," which is what I was
             | responding to, is not idiomatic. Claims about what is and
             | isn't idiomatic aren't really subject to definitive proof
             | either way, but it doesn't matter, because the suggester
             | now agrees that it is not what was meant
             | (https://news.ycombinator.com/item?id=43653723).
        
               | mikelevins wrote:
               | To be clear, the construction I'm endorsing is: "were
               | available only in Lisp until recently", which is the
               | construction that my editors typically proposed for
               | similarly ambiguous deployments of "only". The ambiguity
               | in the original placement is that it could be interpreted
               | as only available as opposed to available and also
               | something else. My editors always wanted it to be clear
               | exactly what the "only" constrains.
        
           | BalinKing wrote:
           | Personally, I'd probably move "until recently" to the front:
           | "Until recently, properly working lambda expressions were
           | only available in Lisp."
        
             | damnitbuilds wrote:
             | Sorry, I did indeed misunderstand the sentence due to its
             | phrasing. I cannot edit my comment for some reason.
             | 
             | I like your fix the most.
        
         | argentier wrote:
         | "properly working lambda expressions were available only in
         | lisp until recently."
        
       | efitz wrote:
       | This is the first article I've ever read that made me want to go
       | learn Lisp.
        
         | cutler wrote:
         | Watch Rich Hickey's early Clojure videos and be blown away.
        
           | laurent_du wrote:
           | Got any specific suggestion?
        
             | spicybbq wrote:
             | "Simple Made Easy" is pretty popular, there is a
             | transcription with slides:
             | 
             | https://github.com/matthiasn/talk-
             | transcripts/blob/master/Hi...
        
               | filoeleven wrote:
               | I like "Clojure, Made Simple" even more.
               | 
               | https://www.youtube.com/watch?v=028LZLUB24s
               | 
               | Someone helpfully pulled out this chunk, which is a good
               | illustration of why data is better than functions, a key
               | driver of Clojure's design.
               | 
               | https://www.youtube.com/watch?v=aSEQfqNYNAc
        
             | cess11 wrote:
             | It's tangentially relevant, but I've enjoyed this one,
             | about hammock driven programming.
             | 
             | https://www.youtube.com/watch?v=f84n5oFoZBc
        
         | dutchblacksmith wrote:
         | Lispworks has a free editition with lots of examples. Look into
         | PAIP from Peter Norvig.
        
           | larve wrote:
           | Even putting the common lisp aside, PAIP is my favourite book
           | about programming in general, by FAR. Norvig's programming
           | style is so clear and expressive, the book touches on more
           | "pedestrian" parts of programming: building tools /
           | performance / debugging, but also walks you through a serious
           | set of algorithms that are actually practical and that I use
           | regularly (and they shape your thinking): search, pattern
           | matching, to some extent unification, building interpreters
           | and compilers, manipulating code as data.
           | 
           | It's also extremely fun, you go from building Eliza to a full
           | pattern matcher to a planning agent to a prolog compiler.
        
         | phlakaton wrote:
         | Paul Graham's _On Lisp_ is also a powerful argument to try the
         | language, even if some of the stuff it presents is totally
         | bonkers. :-D
         | 
         | https://www.paulgraham.com/onlisp.html
        
           | psb wrote:
           | what are the bonkers parts? (just curious)
        
         | __MatrixMan__ wrote:
         | Next time you see a HN post on a lisp-centric topic, click into
         | the comments. I'll bet you a nickel that they'll be happier
         | than most. Instead of phrases like "dumpster fire" they're
         | using words like "joyful".
         | 
         | That's why I keep rekindling my learn-lisp effort. It feels
         | like I'm just scratching the surface re: the fun that can be
         | had.
        
           | dutchblacksmith wrote:
           | Never been happier since building an Erp system in pure lisp
           | and postgresql.
        
       | terminalbraid wrote:
       | > Other general purpose languages are more popular and ultimately
       | can do everything that Lisp can (if Church and Turing are
       | correct).
       | 
       | I find these types of comments extremely odd and I very much
       | support lisp and lisp-likes (I'm a particular fan of clojure). I
       | can only see adding the parenthetical qualifier as a strange bias
       | of throwing some kind of doubt into other languages which is
       | unwarranted considering lisp at its base is usually implemented
       | in those "other general purpose languages".
       | 
       | If you can implement lisp in a particular language then that
       | particular language can de facto do (at least!) everything lisp
       | can do.
        
         | zachbeane wrote:
         | Common Lisp at its base is usually written in Common Lisp.
        
           | terminalbraid wrote:
           | I'm sure you are aware there is ultimately a chicken and egg
           | problem here. Even given the case you presented, it doesn't
           | invalidate the point that if it can implement lisp it must be
           | able to do everything lisp can do. In fact given lisp's
           | simplicity, I'd be hard pressed to call a language that
           | _couldn 't_ implement lisp "general purpose".
        
             | greydius wrote:
             | "You're a very clever man, Mr. James, and that's a very
             | good question," replied the little old lady, "but I have an
             | answer to it. And it's this: The first turtle stands on the
             | back of a second, far larger, turtle, who stands directly
             | under him."
             | 
             | "But what does this second turtle stand on?" persisted
             | James patiently.
             | 
             | To this, the little old lady crowed triumphantly,
             | 
             | "It's no use, Mr. James--it's turtles all the way down."
        
             | grandempire wrote:
             | > I'm sure you are aware there is ultimately a chicken and
             | egg problem here.
             | 
             | You should learn more about compilers. There is a really
             | cool idea waiting for you.
        
         | pgwhalen wrote:
         | Isn't this just a cheeky joke? I.e. "if Einstein is right about
         | this whole theory of relatively thing"
        
         | volemo wrote:
         | I believe 'twas a joke.
        
         | russellbeattie wrote:
         | One doesn't have to invoke Turing or Church to show all
         | languages can do the same things.
         | 
         | Any code that runs on a computer (using the von Neumann
         | architecture) boils down to just a few basic operations:
         | Read/write data, arithmetic (add/subtract/etc.), logic
         | (and/or/not/etc.), bit-shifting, branches and jumps. The rest
         | is basically syntactic sugar or macros.
         | 
         | If your preferred programming language is a pre-compiled type-
         | safe object oriented monster with polymorphic message passing
         | via multi-process co-routines, or high-level interpreted purely
         | functional archetype of computing perfection with just two
         | reserved keywords, or even just COBOL, it's all going to break
         | down eventually to the ops above.
        
           | terminalbraid wrote:
           | You can trivially devise a language that doesn't, though?
           | Let's say I have a language that can return 0 and only 0. It
           | cannot reproduce lisp.
        
           | Capricorn2481 wrote:
           | Sometimes, when people say one language can't do what another
           | does, they aren't talking about outputs. Nobody is arguing
           | that lisp programs can do arithmetic and others can't,
           | they're arguing that there are ergonomics to lisp you can't
           | approach in other languages.
           | 
           | But even so
           | 
           | > it's all going to break down eventually to the ops above.
           | 
           | That's not true either. Different runtimes will break down
           | into a completely different version of the above. C is going
           | to boil down to a different set of instructions than Ruby.
           | That would make Ruby incapable of doing some tasks, even with
           | a JIT. And writing performance sensitive parts in C only
           | proves the point.
           | 
           | "Any language can do anything" is something we tell juniors
           | who have decision paralysis on what to learn. That's good for
           | them, but it's not true. I'm not going to tell a client we're
           | going to target a microcontroller with PHP, even if someone
           | has technically done it.
        
         | varjag wrote:
         | There are several Lisp implementations (including fully-fledged
         | operating systems) which are implemented in Lisp top to bottom.
        
         | taeric wrote:
         | This is conflating slightly different things, though? One is
         | that you can build a program that does the same thing. The
         | other is that you can do the same things with the language.
         | 
         | There are special forms in LISP, but that is a far cry from the
         | amount of magic that can only be done in the compiler or at
         | runtime for many languages out there.
        
           | thethimble wrote:
           | Brainfuck is also Turing complete but that isn't an argument
           | that it's a good replacement for LISP or any other language.
        
             | f1shy wrote:
             | That has a name: Turing tarpit.
        
         | bitwize wrote:
         | Yes, but sometimes doing the things Lisp can do in another
         | language as easily and flexibly as they are done in Lisp has,
         | as a first step, _implementing Lisp in the target language_.
         | 
         | For a famous example, see Clasp: https://github.com/clasp-
         | developers/clasp
        
       | lukaslalinsky wrote:
       | Whenever I hear someone talking about purely functional
       | programming, no side effects, I wonder what kind of programs they
       | are writing. Pretty much anything I've written over the last 30
       | years, the main purpose was to do I/O, it doesn't matter whether
       | it's disk, network, or display. And that's where the most
       | complications come from, these devices you are communicating with
       | have quirks that need you need to deal with. Purely functional
       | programming is very nice in theory, but how far can you actually
       | get away with it?
        
         | throw0101d wrote:
         | > _Pretty much anything I 've written over the last 30 years,
         | the main purpose was to do I/O, it doesn't matter whether it's
         | disk, network, or display._
         | 
         | Erlang is a strictly (?) a functional language, and the reason
         | why it was invented was to do network-y stuff in the telco
         | space. So I'm not sure why I/O and functional programming would
         | be opposed to each other like you imply.
        
           | troupo wrote:
           | > Erlang is a strictly (?) a functional language,
           | 
           | First and foremost Erlang is a _pragmatic_ programming
           | language :)
        
         | mejutoco wrote:
         | Any useful program has side-effects. IMHO the point is to
         | isolate the part of the code that has the side-effects as much
         | as possible, and keep the rest purely functionsl. That makes it
         | easier to debug, test, and create good abstractions. Long term
         | it is a very good approach.
        
         | fulafel wrote:
         | This is discussing Common Lisp which is not even a mostly-
         | functional language, and far from purely functional.
        
           | vkazanov wrote:
           | Somehow haskell and friends shifted the discussion around
           | functional programming to pure vs non-pure! I am pretty sure
           | it started with functions as first order objects as
           | differentiator in schemes, lisps and ml family languages.
           | Thus functional, but that's just a guess.
        
             | mrkeen wrote:
             | > Somehow haskell and friends shifted the discussion around
             | functional programming to pure vs non-pure
             | 
             | In direct response every other language in the mid 2010s
             | saying, "Look, we're functional too, we can pass functions
             | to other functions, see?"                 foo.bar()
             | .map(x => fireTheMissiles())          .collect();
             | 
             | C's had that forever:                 void qsort(void
             | *base, size_t nmemb, size_t size,                  int
             | (*compar)(const void *, const void *))
        
               | milesrout wrote:
               | No, functions aren't first class in C. When you use a
               | function in an expression it undergoes lvalue conversion
               | and "decays" to a pointer to the function. You can only
               | call, store, etc function pointers, not functions.
               | Function pointers are first class. Functions are not as
               | you can't create them at runtime.
               | 
               | A functional programming language is one with first class
               | functions.
        
               | grandempire wrote:
               | What is the impact on the user of having first class
               | functions vs first class function pointers?
               | 
               | Last I checked when you implement lambda in lisp it's
               | also a pointer to the lambda internally.
        
               | vkazanov wrote:
               | In a way this is true.
               | 
               | A function pointer is already half way there. What it
               | lacks is lexical environment capture.
               | 
               | And things that are possible to do with closures never
               | stop amazing me.
               | 
               | Anyways, functional programming is not about purity. It
               | is something that came from the academia, with 2 major
               | language families: ML-likes and Lisp-likes, each focusing
               | on certain key features.
               | 
               | And purity is not even the key feature of MLs in general.
        
               | kstrauser wrote:
               | Closures bring me joy.
        
               | shadowgovt wrote:
               | They are one of those language features that, having
               | learned them, it's a little hard to flip my brain around
               | into the world I knew before I learned them.
               | 
               | If I think hard, I can _sort_ of remember how I used to
               | do things before I worked almost exclusively in languages
               | that natively support closures ( "Let's see... I create a
               | state object, and it copies or retains reference to all
               | the relevant variables... and for convenience I put my
               | function pointer in there too usually... But I still need
               | rules for disposing the state when I'm done with it..."
               | It's so much nicer when the language handles all of that
               | bookkeeping for you and auto-generates those state
               | constructs).
        
           | mikelevins wrote:
           | He says Lisp, rather than Common Lisp. Sure, given the
           | context he's writing in now, maybe he means Common Lisp, but
           | Joe Marshall was a Lisp programmer before Common Lisp
           | existed, so he may not mean Common Lisp specifically.
        
         | vkazanov wrote:
         | I had the same question until I understood one key pattern of
         | pure functional programming. Not sure it has a name but here
         | goes.
         | 
         | There is world, and there is a model of the world - your
         | program. The point of the program, and all functions, is to
         | interact with the model. This part, data structures and all, is
         | pure.
         | 
         | The world interacts with the model through an IO layer, as in
         | haskell.
         | 
         | Purity is just an enforcement of this separation.
        
           | skydhash wrote:
           | I thinks it the imperative shell, functional core. The shell
           | provide the world, the core act on it, the the shell commit
           | it at various intervals.
           | 
           | Functional React follows this pattern. The issue is when the
           | programmer thinks the world is some kind of stable state that
           | you can store results in. It's not, the whole point is to be
           | created anew and restart the whole computation flow. The
           | escape hatches are the hooks. And each have a specific usage
           | and pattern to follow to survive world recreation. Which why
           | you should be careful with them as they are effectively world
           | for subcomponents. So when you add to the world with hooks,
           | interactions with the addition should stay at the same level
        
         | perrygeo wrote:
         | Not even the most fanatical functional programming zealots
         | would claim that programs can be 100% functional. By
         | definition, a program requires inputs and outputs, otherwise
         | there is literally no reason to run it.
         | 
         | Functional programming simply says: separate the IO from the
         | computation.
         | 
         | > Pretty much anything I've written over the last 30 years, the
         | main purpose was to do I/O, it doesn't matter whether it's
         | disk, network, or display.
         | 
         | Every useful program ever written takes inputs and produces
         | outputs. The interesting part is what you actually do in the
         | middle to transforms inputs -> outputs. And that can be
         | entirely functional.
        
           | markus_zhang wrote:
           | > separate the IO from the computation.
           | 
           | Can you please elaborate on this point? I read it as this web
           | page (https://wiki.c2.com/?SeparateIoFromCalculation)
           | describes, but I fail to see why it is a functional
           | programming concept.
        
             | mrkeen wrote:
             | > I fail to see why it is a functional programming concept.
             | 
             | Excellent! You will encounter 0 friction in using an FP
             | then.
             | 
             | To the extent that programmers find friction using Haskell,
             | it's usually because their computations unintentionally
             | update the state of the world, and the compiler tells them
             | off for it.
        
             | AstroBen wrote:
             | Think about this: if a function calls another function that
             | produces a side effect, both functions become impure (non-
             | functional). Simply separating them isn't enough. That's
             | the difference when thinking of it in functional terms
             | 
             | Normally what functional programmers will do is pull their
             | state and side effects up as high as they can so that most
             | of their program is functional
        
             | teddyh wrote:
             | Having functions which do nothing but computation is core
             | functional programming. I/O should be delegated to the
             | edges of your program, where it is necessary.
        
             | bmacho wrote:
             | > but I fail to see why it is a functional programming
             | concept.
             | 
             | "Functional programming" means that you primarily use
             | functions (not C functions, but mathematical pure
             | functions) to solve your problems.
             | 
             | This means you won't do IO in your computation because you
             | can't do that. It also means you won't modify data, because
             | you can't do that either. Also you might have access to
             | first class functions, and can pass them around as values.
             | 
             | If you do procedural programming in C++ but your functions
             | don't do IO or modify (not local) values, then congrats,
             | you're doing functional programming.
        
               | markus_zhang wrote:
               | Thanks. I now see why it makes sense to me. I work in DE
               | so in most of our cases we do streaming (IO) without any
               | transformation (computation), and then we do
               | transformation in a total different pipeline. We never
               | transform anything we consumed, always keep the original
               | copy, even if it's bad.
        
           | DeathArrow wrote:
           | >Not even the most fanatical functional programming zealots
           | would claim that programs can be 100% functional. By
           | definition, a program requires inputs and outputs, otherwise
           | there is literally no reason to run it.
           | 
           | So a program it's a function that transforms the input to the
           | output.
        
           | DeathArrow wrote:
           | >separate the IO from the computation.
           | 
           | What about managing state? I think that is an important part
           | and it's easy to mess it.
        
             | roxolotl wrote:
             | Each step calculates the next state and returns it. You can
             | then compose those state calculators. If you need to save
             | the state that's IO and you have a bit specifically for it.
        
             | skydhash wrote:
             | It takes a bit of discipline, but generally all state
             | additions should be scoped to the current context. Meaning,
             | when you enter a subcontext, it has become input and
             | treated as holy, and when you leave to the parent context,
             | only the result matters.
             | 
             | But that particular context has become inpure and decried
             | as such in the documentation, so that carefulness is
             | increased when interacting with it.
        
           | whatnow37373 wrote:
           | > The interesting part is what you actually do in the middle
           | to transforms inputs -> outputs.
           | 
           | Can you actually name something? The only thing I can come up
           | with is working with interesting algorithms or
           | datastructures, but that kind of fundamental work is very
           | rare in my experience. Even if you do, it's quite often a
           | very small part of the entire project.
        
             | skydhash wrote:
             | A whole web app. The IO are generally user facing network
             | connections (request and response), IPC and RPC (databases,
             | other services), and files interaction. Anything else is
             | logic. An FP programs is a collection of pipes, and IO are
             | the endpoints. With FP the blob of data passes cleanly from
             | one section to another while in imperative, some of it
             | sticks. In OOP, there's a lot of blob, that flings stuff at
             | each other and in the process create more blobs.
        
               | whatnow37373 wrote:
               | A general "web app"'s germane parts are:
               | 
               | - The part that receives the connection
               | 
               | - The part that sends back a response
               | 
               | - Interacting with other unspecified systems through IPC,
               | RPC or whatever (databases mainly)
               | 
               | The shit in between, calculating a derivative or setting
               | up a fancy data structure of some kind or something, is
               | _interesting_ but how much of that do we actually do as
               | programmers? I 'm not being obtuse - intentionally anyway
               | - I'm actually curious what interesting things functional
               | programmers do because I'm not seeing much of it.
               | 
               | Edit: my point is, you say "Anything else is logic." to
               | which I respond "What's left?"
        
               | williamcotton wrote:
               | There's lots left!
               | 
               | "When a customer in our East Coast location makes this
               | purchase then we apply this rate, blah blah blah".
               | 
               | "When someone with >X karma visits HN they get downvote
               | buttons on comments, blah blah blah".
        
               | skydhash wrote:
               | Yes! In most projects, those requirements are stretched
               | across tecnicalities like IOs. But you can pull them back
               | to the core of your project. It takes effort, but the end
               | result is a pleasure to work with. It can be done with
               | FP, OOP, LP,...
        
               | acuozzo wrote:
               | > calculating a derivative or setting up a fancy data
               | structure of some kind or something, is interesting but
               | how much of that do we actually do as programmers?
               | 
               | A LOT, depending on the domain. There are many R&D and
               | HPC labs throughout the US in which programmers work
               | directly with specialists in the hard sciences. A
               | significant percentage of their work is akin to
               | "calculating a derivative".
        
             | hansvm wrote:
             | It's a matter of framing. Think of any of the following:
             | 
             | - Refreshing daily "points" in some mobile app (handling
             | the clock running backward, network connectivity lapses,
             | ...)
             | 
             | - Deciding whether to send an marketing e-mail (have you
             | been unsubscribed, how recently did you send one, have you
             | sent the same one, should you fail open or closed, is this
             | person receptive to marketing, ...)
             | 
             | - How do you represent a person's name and transform it
             | into the things your system needs (different name fields,
             | capitalization rules, max characters, what it you try to
             | put it on an envelope and it doesn't fit, ...)
             | 
             | - Authorization logic (it's not enough to "just use a
             | framework" no matter your programming style; you'll still
             | have important business logic about who can access what
             | when and how the whole thing works together)
             | 
             | And so on. Everything you're doing is mapping inputs to
             | outputs, and it's important that you at least get it kind
             | of close to correct. Some people think functional
             | programming helps with that.
        
               | whatnow37373 wrote:
               | When I see this list all I can think of is how all these
               | things are just generic, abstract rules and have nothing
               | to do with programming. This, of course, is my problem. I
               | have a strange mental model of things.
               | 
               | I can't shake off the feeling we should be defining some
               | clean sort of "business algebra" that can be used to
               | describe these kind of notions in a proper closed form
               | and can then be used to derive or generate the actual
               | code in whatever paradigm you need. What we call code
               | feels like a distraction.
               | 
               | I am wrong and strange. But thanks for the list, it's
               | helpful and I see FP's points.
        
               | codr7 wrote:
               | I agree.
               | 
               | Especially when reading Rust or C++.
               | 
               | That's code I would prefer to have generated for me as
               | needed in many cases, I'm generally not that interested
               | in manually filling in all the details.
               | 
               | Whatever it is, it hasn't been created yet.
        
               | hansvm wrote:
               | You're maybe strange (probably not, when restricted to
               | people interested in code), but wrongness hasn't been
               | proven yet.
               | 
               | I'd push back, slightly, in that you need to encode those
               | abstract rules _somehow_, and in any modern parlance that
               | "somehow" would be a programming language, even if it
               | looks very different from what we're used to.
               | 
               | From the FP side of things, they'd tend to agree with
               | you. The point is that these really are generic, abstract
               | rules, and we should _just_ encode the rules and not the
               | other state mutations and whatnot that also gets bundled
               | in.
               | 
               | That implicitly assumes a certain rule representation
               | though -- one which takes in data and outputs data. It's
               | perfectly possible, in theory, to describe constraints
               | instead. Looking at the example of daily scheduling in
               | the presence of the clock running backward; you can
               | define that in terms of inputs and outputs, or you can
               | say that the desired result satisfies (a) never less than
               | the wall clock, (b) never decreases, (c) is the minimal
               | such solution. Whether that's right or not is another
               | story (it probably isn't, by itself -- lots of mobile
               | games have bugs like that allowing you to skip ads or
               | payment forever), but it's an interesting avenue for
               | exploration given that those rules can be understood
               | completely orthogonally and are the business rules we
               | _actually_ care about, whereas the FP, OOP, and
               | imperative versions must be holistically analyzed to
               | ensure they satisfy business rules which are never
               | actually written down in code.
        
             | perrygeo wrote:
             | > Even if you do, it's quite often a very small part of the
             | entire project.
             | 
             | So your projects are only moving bits from one place to
             | another? I've literally never seen that in 20 years of
             | programming professionally. Even network systems that are
             | seen as "dumb pipes" need to parse and interpret packet
             | headers, apply validation rules, maintain BGP routing
             | tables, add their own headers etc.
             | 
             | Surely the program calculates something, otherwise why
             | would you need to run the program at all if the output is
             | just a copy of the input?
        
               | whatnow37373 wrote:
               | Yes and I notice you still did not provide an interesting
               | example. Surely parsing packets is not an _interesting_
               | example of functional programming 's powers?
               | 
               | What interesting things do you do as a programmer,
               | really?
        
               | perrygeo wrote:
               | > parse and interpret packet headers, apply validation
               | rules, maintain BGP routing tables, add their own headers
               | etc.
               | 
               | That's a few more than zero. I don't do network
               | programming, that was just an example to show how even
               | the quintessential IO-heavy application requires non-
               | trivial calculations internally.
        
               | whatnow37373 wrote:
               | Fair enough. It's just that in my experience the "cool
               | bits" are quickly done and then we get bogged down in
               | endless layers of inter-systems communication (HTTP, RPC,
               | file systems, caches). I often see FP people saying stuff
               | like "it's not 100% pure, of course there are some
               | _isolated_ side-effects " and I'm thinking.. my brother,
               | I live _inside_ side-effects. The days I can have even a
               | few pure functions are few and far between. I 'm honestly
               | curious what percentage of your code bases can be this
               | pure.
               | 
               | But of course this heavily depends on the domain you are
               | working in. Some people work in simulation or physics or
               | whatever and that's where the interesting bits begin.
               | (Even then I'm thinking "programming" is not the
               | interesting bit, it's the physics)
        
               | skydhash wrote:
               | > _The days I can have even a few pure functions are few
               | and far between. I 'm honestly curious what percentage of
               | your code bases can be this pure._
               | 
               | A big part of it, I'm sure, but it requires some work.
               | Pushing the side effects to the edge requires some
               | abstractions to not directly mess with the original
               | mutable state.
               | 
               | You are, in fact designing a state diagram from something
               | that was evolving continuously on a single dimension:
               | time. The transition of the state diagram are the code
               | and the node are the inputs and output of that code. Then
               | it became clear that IOs only matters when storing and
               | loading those nodes. Because those nodes are finite and
               | well defined, then the non-FP code for dealing with them
               | became simpler to write.
        
             | phlakaton wrote:
             | You can name almost anything (these are general-purpose
             | languages, after all), but I'll just throw a couple of
             | things out there:
             | 
             | 1. A compiler. The actual algorithms and datastructures
             | might not be all that interesting (or they might be if
             | you're really interested in that sort of thing), but the
             | kinds of transformations you're doing from stage to stage
             | are sophisticated.
             | 
             | 2. An analytics pipeline. If you're working in the
             | Spark/Scala world, you're writing high-level functional
             | code that represents the transformation of data from input
             | to output, and the framework is compiling it into a
             | distributed program that loads your data across a cluster
             | of nodes, executes the necessary transformations, and
             | assembles the results. In this case there is a ton of
             | stateful I/O involved, all interleaved with your code, but
             | the framework abstracts it away from you.
        
               | whatnow37373 wrote:
               | Thanks, especially two is very interesting. Admittedly
               | the framework itself is the actually interesting part and
               | that's what I meant with this work being "rare" (I mean
               | how many people work on those kinds of frameworks
               | fulltime? It's not zero, but..)
               | 
               | I think what I engaged with is the notion that most
               | programming "has some side-effects" ("it's not 100%
               | pure"), but much of what I see is like 95% side-effects
               | with some cool, interesting bits stuffed in between the
               | endless layers of communication (without which the
               | "interesting" stuff won't be worth shit).
               | 
               | I feel FP is very, very cool if you got yourself isolated
               | in one of those _interesting layers_ but I feel that 's a
               | rare place to be.
        
           | sync13298 wrote:
           | > Every useful program ever written takes inputs and produces
           | outputs. The interesting part is what you actually do in the
           | middle to transforms inputs -> outputs. And that can be
           | entirely functional.
           | 
           | My work needs pseudorandom numbers throughout the big middle,
           | for example, drawing samples from probability distributions
           | and running randomized algorithms. That's pretty messy in a
           | FP setting, particularly when the PRNGs get generated within
           | deeply nested libraries.
        
             | zelphirkalt wrote:
             | At what point does this get messy?
        
               | sync13298 wrote:
               | When deeply nested libraries generate PRNGs, all that
               | layering becomes impure and must be treated like any
               | other stateful or IO code. In Haskell, that typically
               | means living with a monad transformer or effect system
               | managing the whole stack, and relatively little pure code
               | remains.
               | 
               | The messiness gets worse when libraries use different
               | conventions to manage their PRNG statefulness. This is a
               | non-issue in most languages but a mess in a 100% pure
               | setting.
        
         | PhilipRoman wrote:
         | There is no reason you can't use side effects in pure
         | functional programming. You just need to provide the
         | appropriate description of the side effect to avoid caching and
         | force a particular evaluation order. If you have linear types,
         | you do it by passing around opaque tokens. I'm not entirely
         | sure how IO works in Haskell, but I think the implementation is
         | similar. Even C compilers use a system like that internally.
        
         | mrkeen wrote:
         | Think of it like other features:
         | 
         | * Encapsulation? What's the point of having it if's perfectly
         | sealed off from the world? Just dead-code eliminate it.
         | 
         | * Private? It's not really private if I can Get() to it. I want
         | access to that variable, so why hide it from myself? Private
         | adds nothing because I can just choose not to use that
         | variable.
         | 
         | * Const? A constant _variable_ is an oxymoron. All the programs
         | I write change variables. If I want a variable to remain the
         | same, I just wont update it.
         | 
         | Of course I don't believe in any of the framings above, but
         | it's how arguments against FP typically sound.
         | 
         | Anyway, the above features are small potatoes compared to the
         | big hammer that is functional purity: _you (and the compiler)
         | will know and agree upon whether the same input will yield the
         | same output._
         | 
         | Where am I using it right now?
         | 
         | I'm doing some record linkage - matching old transactions with
         | new transactions, where some details may have shifted. I say
         | "shifted", but what really happened was that upstream decided
         | to _mutate_ its data in-place. If they 'd had an FPer on the
         | team, they would not have mutated shared state, and I wouldn't
         | even need to do this work. But I digress.
         | 
         | Now I'm trying out Dijkstra's algorithm, to most efficiently
         | match pairs of transactions. It's a search algorithm, which
         | tries out different alternatives, so it can never mutate things
         | in-place - mutating inside one alternative will silently break
         | another alternative. I'm in C#, and was pleasantly surprised
         | that ImmutableList etc actually exist. But I wish I didn't have
         | to be so vigilant. I really miss Haskell doing that part of my
         | carefulness for me.
        
           | DeathArrow wrote:
           | >I'm in C#, and was pleasantly surprised that ImmutableList
           | etc actually exist.
           | 
           | C# has introduced many functional concepts. Records, pattern
           | matching, lambda functions, LINQ.
           | 
           | The only thing I am missing and will come later is
           | discriminated unions.
           | 
           | Of course, F# is more fited for the job if you want a mostly
           | functional workflow.
        
             | mrkeen wrote:
             | I don't want functional-flavoured programming, I want
             | functional programming.
             | 
             | Back when I was more into pushing Haskell on my team (10+
             | years ago), I pitched the idea something like:
             | You get: the knowledge that your function's output will
             | only depend on its input.            You pay: you gotta
             | stop using those for-loops and [i]ndexes, and start using
             | maps, folds, filters etc.
             | 
             | Those higher-order functions are a tough sell for
             | programmers who only ever want to do things the way they've
             | always done them.
             | 
             | But 5 years after that, in Java-land everyone was using
             | maps, folds and filters like crazy (Or in C# land, Selects
             | and Wheres and SelectManys etc,) with some half-thought-out
             | bullshit reasoning like "it's functional, so it must good!"
             | 
             | So we paid the price, but didn't get the reward.
        
               | hnra wrote:
               | Using map, fold etc. is not the hard part of functional
               | programming. The hard part is managing effects (via
               | monads, monad transformers, or effects). Trying to
               | convert a procedural inner mutating algorithm to say
               | Haskell is challenging.
        
               | mrkeen wrote:
               | > The hard part is managing effects
               | 
               | You can say that again!
               | 
               | Right now I'm working in C#, so I wished my C# managed
               | effects, but it doesn't. It's all left to the programmer.
        
               | codr7 wrote:
               | I don't know, stacking monads is a comparable level of
               | pain to me.
        
               | troupo wrote:
               | > I don't want functional-flavoured programming, I want
               | functional programming.
               | 
               | > you gotta stop using those for-loops and [i]ndexes, and
               | start using maps, folds, filters etc.
               | 
               | You mean what C# literally does everywhere because
               | Enumerable is the premier weapon of choice in the
               | language, and has a huge amount of exactly what you want:
               | https://learn.microsoft.com/en-
               | us/dotnet/api/system.linq.enu...
               | 
               | (well, with the only exception of foreach which is for
               | some odd reason is still a loop).
               | 
               | > But 5 years after that
               | 
               | Since .net 3.5 18 years ago:
               | https://learn.microsoft.com/en-
               | us/dotnet/api/system.linq.enu...
               | 
               | > So we paid the price, but didn't get the reward.
               | 
               | Who is "we", what was the price, and what was the
               | imagined reward?
        
               | mrkeen wrote:
               | > Who is "we", what was the price, and what was the
               | imagined reward?
               | 
               | Slow down and re-read.
               | 
               | >> You get: the knowledge that your function's output
               | will only depend on its input.
               | 
               | >> You pay: you gotta stop using those for-loops and
               | [i]ndexes, and start using maps, folds, filters etc.
        
               | BeetleB wrote:
               | > You get: the knowledge that your function's output will
               | only depend on its input.
               | 
               | > You pay: you gotta stop using those for-loops and
               | [i]ndexes, and start using maps, folds, filters etc.
               | 
               | You're my type of guy. And literally _none_ of my
               | coworkers in the last 10 years were your type of guy.
               | When they read this, they don 't look at it in awe, but
               | in horror. For them, functions _should_ be allowed to
               | have side effects, and for loops is a basic thing they
               | don 't see good reason to abandon.
        
               | zelphirkalt wrote:
               | Statistically most of ones coworkers will never have
               | looked at and used to write actual code with a functional
               | language, so it is understandable they don't get it. What
               | makes me sad is the apparent unwillingness to learn such
               | a thing and sticking with "everything must OOP" even in
               | situations where it would be (with a little practice and
               | knowledge in functional languages) simple to make it
               | purely functional and make testing and parallelization
               | trivial.
        
               | BeetleB wrote:
               | > Statistically most of ones coworkers will never have
               | looked at and used to write actual code with a functional
               | language, so it is understandable they don't get it.
               | 
               | I'm not against functional languages. My point was that
               | if you want to encourage others to try it, those two are
               | _not_ what you want to lead with.
        
               | tpmoney wrote:
               | One struggle I've had with wrapping my head around using
               | FP and lisp like languages for a "real world" system is
               | handling something like logging. Ideally that's handled
               | outside of the function that might be doing a data
               | transformation but how do you build a lot message that
               | outputs information about old and new values without
               | contamination of your "pure" transducer?
               | 
               | You could I guess have a "before" step that iterates your
               | data stream and logs all the before values, and then an
               | "after" step that iterates after and logs all the after
               | and get something like:
               | 
               | ``` (->> (map log-before data) (map transform-data) (map
               | log-after-data)) ```
               | 
               | But doesn't that cause you to iterate your data 2x more
               | times than you "need" to and also split your logging into
               | 2x as many statements (and thus 2x as much IO)
        
               | trealira wrote:
               | So, do you mean like you have some big array, and you
               | want to do something like this? (Below is not a real
               | programming language.)                 for i in 0 to
               | arr.len() {           new_val = f(arr[i]);
               | log("Changing {arr[i]} to {new_val}.\n");
               | arr[i] = new_val;       }
               | 
               | I haven't used Haskell in a long time, but here's a kind
               | of pure way you might do it in that language, which I got
               | after tinkering in the GHCi REPL for a bit. In Haskell,
               | since you want to separate IO from pure logic as much as
               | possible, functions that would do logging return instead
               | a tuple of the log to print at the end, and the pure
               | value. But because that's annoying and would require
               | rewriting a lot of code manipulating tuples, there's a
               | monad called the Writer monad which does it for you, and
               | you extract it at the end with the `runWriter` function,
               | which gives you back the tuple after you're done doing
               | the computation you want to log.
               | 
               | You shouldn't use Text or String as the log type, because
               | using the Writer involves appending a lot of strings,
               | which is really inefficient. You should use a Text
               | Builder, because it's efficient to append Builder types
               | together, and because they become Text at the end, which
               | is the string type you're supposed to use for Unicode
               | text in Haskell.
               | 
               | So, this is it:                 import qualified
               | Data.Text.Lazy as T       import qualified
               | Data.Text.Lazy.Builder as B       import qualified
               | Data.Text.Lazy.IO as TIO       import
               | Control.Monad.Writer              mapWithLog ::
               | (Traversable t, Show a, Show b) => (a -> b) -> t a ->
               | Writer B.Builder (t b)       mapWithLog f = mapM helper
               | where           helper x = do              let x' = f x
               | tell (make x <> B.fromString " becomes " <> make x' <>
               | B.fromString ". ")             pure x'           make x =
               | B.fromString (show x)            theActualIOFunction list
               | = do         let (newList, logBuilder) = runWriter
               | (mapWithLog negate list)         let log = B.toLazyText
               | logBuilder         TIO.putStrLn log         -- do
               | something with the new list...
               | 
               | So "theActualIOFunction [1,2,3]" would print:
               | 1 becomes -1. 2 becomes -2. 3 becomes -3.
               | 
               | And then it does something with the new list, which has
               | been negated now.
        
           | pfdietz wrote:
           | Those starred rhetorical questions initially looked to me
           | like a critique of Lisp! Because that's how Lisp
           | (particularly Common Lisp) works. All those things are
           | softish. You can see unexported symbols even if you're not
           | supposed to use them. There is no actual privacy unless you
           | do something special like unintern then recreate a symbol.
        
         | Xmd5a wrote:
         | The boundary between the program and the rest of the system
         | allows I/O of course. What FP does is "virtualize" I/O by
         | representing it as data (thus it can be passed around). Then at
         | some point these changes get "committed" to the outside.
         | Representing I/O separately from how it is carried out allows a
         | lot of things to be done, such as cancelling (ctrl+z)
         | operations.
        
         | AstroBen wrote:
         | It's about minimizing and isolating state and side effects, not
         | eliminating them completely
         | 
         | Functional core, imperative shell is a common pattern. Keep the
         | side effects on the outside. Instead of doing side effects
         | directly, just return a data structure that can be used to
         | enact the side effect
        
         | zozbot234 wrote:
         | The point of functional, "sans I/O" style is to separate the
         | definition of I/O from the rest of your logic. You're still
         | doing I/O, but _what_ sorts of I /O you're doing has a clear
         | self-contained definition within your program. https://sans-
         | io.readthedocs.io/how-to-sans-io.html
        
         | brap wrote:
         | There are ways to handle side effects with pure functions only
         | (it's kind of cheating, because the actual side effects are
         | performed by the non-pure runtime/framework that's abstracted
         | away, while the pure user code just defines when to perform
         | them and how to respond to them). It's possible, but it gets
         | very awkward very fast. I wouldn't use FP for any part of the
         | code that deals with IO.
        
         | hnfong wrote:
         | I never believed any FP evangelist ever since I realized I
         | can't even write quicksort with it *.
         | 
         | (* Yes, you can technically write it procedurally like a good C
         | programmer, sure.)
        
         | larve wrote:
         | It's always hard to parse if people mean functional programming
         | when bringing up Lisp. Common Lisp certainly is anything but a
         | functional language. Sure, you have first order functions, but
         | you in a way have that in pretty much all programming languages
         | (including C!).
         | 
         | But most functions in Common Lisp do mutate things, there is an
         | extensive OO system and the most hideous macros like LOOP.
         | 
         | I certainly never felt constrained writing Common Lisp.
         | 
         | That said, there are pretty effective patterns for dealing with
         | IO that allow you to stay in a mostly functional /
         | compositional flow (dare I say monads? but that sounds way more
         | clever than it is in practice).
        
           | behnamoh wrote:
           | > It's always hard to parse if people mean functional
           | programming when bringing up Lisp. Common Lisp certainly is
           | anything but a functional language. Sure, you have first
           | order functions, but you in a way have that in pretty much
           | all programming languages (including C!).
           | 
           | It's less about what the language "allows" you to do and more
           | about how the ecosystem and libraries "encourage" you to do.
        
         | jcranmer wrote:
         | The idea of pure functional programming is that you can really
         | go quite far if you think of your program as a pure function
         | f(input) -> outputs with a messy impure thing that calls f and
         | does the necessary I/O before/after that.
         | 
         | Batch programs are easy to fit in this model generally. A
         | compiler is pretty clearly a pure function f(program source
         | code) -> list of instructions, with just a very thin layer to
         | read/write the input/output to files.
         | 
         | Web servers can often fit this model well too: a web server is
         | an f(request, database snapshot) -> (response, database
         | update). Making that work well is going to be gnarly in the
         | impure side of things, but it's going to be quite doable for a
         | lot of basic CRUD servers--probably every web server I've ever
         | written (which is a lot of tiny stuff, to be fair) could be
         | done purely functional without much issue.
         | 
         | Display also can be made work: it's f(input event, state) ->
         | (display frame, new state). Building the display frame here is
         | something like an immediate mode GUI, where instead of mutating
         | the state of widgets, you're building the entire widget tree
         | from scratch each time.
         | 
         | In many cases, the limitations of purely functional isn't that
         | somebody somewhere has to do I/O, but rather the impracticality
         | of faking immutability if the state is too complicated.
        
           | lukaslalinsky wrote:
           | I guess my point is that you actually have to write the
           | impure code somehow and it's hard, external world has
           | tendencies to fail, needs to be retried, coordinated with
           | other things. You have to fake all these issues. In your web
           | server examples, if you need to a cache layer for certain
           | part of the data, you really can't without encoding it to the
           | state management tooling. And this point you are writing a
           | lot of non-functional code in order to glue it together with
           | pure functions and maybe do some simple transformation in the
           | middle. Is it worth it?
           | 
           | I have respect for OCaml, but that's mostly because it allows
           | you to write mutable code fairly easily.
           | 
           | Roc codifies the world vs core split, but I'm skeptical how
           | much of the world logic can be actually reused across
           | multiple instances of FP applications.
        
             | eduction wrote:
             | There's a spectrum of FP languages with Haskell near the
             | "pure" end where it truly becomes a pain to do things like
             | io and Clojure at the more pragmatic end where not only is
             | it accepted that you'll need to do non functional things
             | but specific facilities are provided to help you do them
             | well and in a way that can be readily brought into the
             | functional parts of the language.
             | 
             | (I'm biased though as I am immersed in Clojure and have
             | never coded in Haskell. But the creator of Clojure has gone
             | out of his way to praise Haskell a bunch and openly admits
             | where he looked at or borrowed ideas from it.)
        
         | timonoko wrote:
         | OpenSCAD is such a good school of functional programming. There
         | is no "time" or flow of execution. Or variables, scopes and
         | environments. You are not constructing a program, but a static
         | model which has desired properties in space and time.
        
         | diggan wrote:
         | > Whenever I hear someone talking about purely functional
         | programming, no side effects, I wonder what kind of programs
         | they are writing
         | 
         | Where have you ever heard anyone talk about side-effect free
         | programs, outside of academic exercises? The linked post
         | certainly isn't about 100% side-effect/state free code.
         | 
         | Usually, people talk about minimizing side-effects as much as
         | possible, but since we build programs to do something,
         | sometimes connected to the real world, it's basically
         | impossible to build a program that is both useful and 100%
         | side-effect free, as you wouldn't be able to print anything to
         | the screen, or communicate with other programs.
         | 
         | And minimizing side-effects (and minimizing state overall) have
         | a real impact on how easy it is to reason about the program.
         | Being really carefully about where you mutate things, leads to
         | most of the code being very explicit about what it's doing, and
         | code only affects data that is close to where the code itself
         | is, compared to intertwined state mutation, where things
         | everywhere in the codebase can affect state anywhere.
        
         | djha-skin wrote:
         | I wrote a recursive descent parser in Lisp for a YAML
         | replacement language[1]. It wasn't difficult. Lisp makes it
         | easy to write I/O, but also easy to separate logic from I/O.
         | This made it easy for me to write unit tests without mocking.
         | 
         | I also wrote a toy resource scheduler at an HTTP endpoint in
         | Haskell[2]. Writing I/O in Haskell was a learning curve but was
         | ultimately fine. Keeping logic separate from I/O was the easy
         | thing to do.
         | 
         | 1: https://github.com/djha-skin/nrdl
         | 
         | 2: https://github.com/djha-skin/lighthouse
        
         | epgui wrote:
         | Everyone writes real programs that have side effects.
         | Functional programming is no different. But the side effects
         | happen in specific ways or places, rather than all over the
         | place.
        
         | johnnyjeans wrote:
         | As others have said, a pure program is a useless program. The
         | only place stuff like that has in this world is as a proof
         | assistant.
         | 
         | What I will add is look up how the GHC runtime works, and the
         | STGM. You may find it extremely interesting. I didn't "get"
         | functional programming until I found out about how exotic
         | efficient execution of functional programs ends up being.
        
         | yogsototh wrote:
         | "Purely Functional Programming", I guess mostly
         | Haskell/Purescript.
         | 
         | So this only really mean:
         | 
         | Purely Functional Programming by default.
         | 
         | In most programming languages you can write
         | 
         | "hello " + readLine()
         | 
         | And this would intermix pure function (string concatenation)
         | and impure effect (asking the user to write some text). And
         | this would work perfectly.
         | 
         | By doing so, the order of evaluation becomes essential.
         | 
         | With a pure functional programming (by default).
         | 
         | you must explicitely separate the part of your program doing
         | I/O and the part of your program doing only pure computation.
         | And this is enforced using a type system focusing on I/O. Thus
         | the difference between Haskell default `IO` and OCamL that does
         | not need it for example.
         | 
         | in Haskell you are forced by the type system to write something
         | like:                   do            name <- getLine
         | let s = "Hello " <> name <> "!"           putStrLn s
         | 
         | you cannot mix the `getLine` directly in the middle of the
         | concatenation operation.
         | 
         | But while this is a very different style of programming, I/O
         | are just more explicit, and they "cost" more, because writing
         | code with I/O is not as elegant, and easy to manipulate than
         | pure code. Thus it naturally induce a way of coding that try to
         | really makes you conscious about the part of your program that
         | need IO and the part that you could do with only pure function.
         | 
         | In practice, ... yep, you endup working in a "Specific to your
         | application domain" Monad that looks a lot like the IO Monad,
         | but will most often contains IO.
         | 
         | Another option is to use a free monad for your entire program
         | that makes you able to write in your own domain language and
         | control its evaluation (either using IO or another system that
         | simulates IO but is not really IO, typically for testing
         | purpose).
        
         | dehrmann wrote:
         | The pragmatic approach is to see that FP's key point is
         | statelessness and use that in your code (written in more
         | mainstream languages) when appropriate.
        
         | krferriter wrote:
         | Most of the code in most programs is not the part that is doing
         | the I/O. It's doing stuff on a set of values to transform them.
         | It gets values from somewhere, does stuff using those values,
         | and then outputs some values. The complicated part is not the
         | transfer of the final byte sequence to whatever I/O interface
         | they go to, the core behavior of the program is the stuff that
         | happens before that.
        
       | zoky wrote:
       | I'm not really familiar with Lisp, but from glancing at this
       | article it seems like all of these are really good arguments for
       | programming in Ruby (my language of choice). Easily predictable
       | syntax, simple substitution between variables and method calls,
       | dynamic typing that provides ad hoc polymorphism... these are all
       | prominent features of Ruby that are much clunkier in Python,
       | JavaScript, or really any other commonly used language that I can
       | think of.
       | 
       | Lisp is on my list of languages to learn someday, but I've
       | already tried to pick up Haskell, and while I did enjoy it and
       | have nothing but respect for the language, I ultimately abandoned
       | it because it was just too time-consuming for me to use on a day-
       | to-day basis. Although I definitely got something out of learning
       | to program in a purely functional language, and in fact feel like
       | learning Haskell made me a much better Ruby programmer.
        
         | rjsw wrote:
         | Common Lisp has compilers that produce fast code.
        
           | shadowgovt wrote:
           | On this topic: My absolute favorite Common Lisp special
           | operator is `(the`. Usage, `(the value-type form`, as in
           | `(the integer (do-a-bunch-of-math))`.
           | 
           | At first glance, it looks like your strong-typing tool. And
           | it _can_ be. You can build a static analyzer that will match,
           | as best it can, the type of the form to the value-type and
           | throw errors if they don 't match. It can also be a runtime
           | check; the runtime _is allowed to_ treat `the` as an assert
           | and throw an error if there 's a mismatch.
           | 
           | But what the spec _actually says_ is that it 's a special
           | operator that does nothing but return the evaluation of the
           | form if the form's value is of the right type and _the
           | behavior is undefined otherwise._ So relative to, say, C++,
           | it 's a clean entrypoint for undefined behavior; it signals
           | to a Lisp _compiler or interpreter_ "Hey, the programmer
           | allows you to do shenanigans here to make the code go faster.
           | Throw away the runtime type identifier, re-represent the data
           | in a faster bit-pattern, slam this value into functions
           | without runtime checks, assume particular optimizations will
           | succeed, paint it red to make it go fasta... Just, go nuts."
        
         | hxegon wrote:
         | I have about 6 years of ruby experience and if you're saying
         | that ruby has "easily predictable syntax"...
         | 
         | You really should try lisp. I liked clojure a lot coming from
         | ruby because it has a lot of nice ergonomics other lisps lack.
         | I think youd get a lot out of it.
        
           | shadowgovt wrote:
           | Ruby and LISP have a lot of overlap. To my money, LISP is a
           | _little_ more predictable because the polymorphic nature of
           | the language itself is always in your face; you know that you
           | 're always staring at a list, and you have no idea without
           | context whether that list is being evaluated at runtime, is
           | literal, or is the body of a macro.
           | 
           | Ruby has all those features but (to my personal taste) makes
           | it less obvious that things are that wilding.
           | 
           | (But in both languages I get to play the game "Where the hell
           | is this function or variable defined?" way more often than I
           | want to. There are some advantages to languages that have a
           | strict rule about modular encapsulation and requiring almost
           | everything into the current context... With Rails, in
           | particular, I find it hard to understand other people's code
           | because I never know if a given symbol was defined in another
           | file in the codebase, defined in a library, or magicked into
           | being by doing string transformations on a data source... In
           | C++, I have to rely on grep a lot to find definitions, but in
           | Ruby on Rails not even grep is likely to find me the answer I
           | want! Common LISP is similarly flexible with a lot of common
           | library functions that magick new symbols into existence, but
           | the codebases I work on in LISP aren't as large as the Ruby
           | on Rails codebases I touch, so it bites me less).
        
             | speedbird wrote:
             | Go back to the source, use Smalltalk in a nice environment
             | like VisualWorks and get all that built in :-)
        
         | cess11 wrote:
         | If you want something similar to Ruby but more functional, try
         | Elixir. The similarities are superficial but might be enough to
         | ease you in.
         | 
         | Haskell is weird. You can express well defined problems with
         | relative ease and clarity, but performance can be kind of wonky
         | and there's a lot more ceremony than your typical Lisp or
         | Scheme or close relative of those. F# can give you a more
         | lispish experience with a threshold about as low as Haskell,
         | but comes with the close ties to an atrocious corporation and
         | similar to Clojure it's not exactly a first class citizen in
         | the environment.
         | 
         | Building stuff in Lisp-likes typically don't entail the double
         | programming languages of the primary one and a second one to
         | program the type system, in that way they're convenient in a
         | way similar to Ruby. I like the parens and how they explicitly
         | delimit portions that quite closely relate to the AST-step in
         | compilation or whatever the interpreter sees, it helps with
         | moving things around and molding the code quickly.
        
       | revskill wrote:
       | Programming is about coordination between tasks. Prove me wrong.
        
         | whatnow37373 wrote:
         | It doesn't have to be wrong to be irrelevant.
         | 
         | Many things _can_ be viewed as coordination problems. All of
         | life can be viewed as being about coordination between tasks.
         | 
         | But I want to engage in good faith and assume you have some way
         | of making this productive. What angle are you going for?
        
           | revskill wrote:
           | Hm so your point is life is programming ?
        
             | whatnow37373 wrote:
             | Isn't that obvious? :)
             | 
             | I do wonder what happens if one of the tasks to be
             | coordinated is "programming"?
        
               | nocman wrote:
               | that's metaprogramming :D
        
               | revskill wrote:
               | No. It's deep philosophy on software design.
        
       | 0xTJ wrote:
       | I've never programmed in a Lisp, but I'd love to learn, it feels
       | like one of those languages like Perl that are just good to know.
       | I do have a job where getting better with SKILL would be useful.
        
       | djha-skin wrote:
       | I agree with some statements OP makes but not others. Ultimately,
       | I write in lisp because it's fun to write in Lisp due to its
       | expressive power, ease of refactoring, and the Lisp Discord[1].
       | 
       | > Lisp is easier to remember,
       | 
       | I don't feel this way. I'm always consulting the HyperSpec or
       | googling the function names. It's the same as any other
       | dynamically typed language, such as Python, this way to me.
       | 
       | > has fewer limitations and hoops you have to jump through,
       | 
       | Lisp as a language has incredibly powerful features find nowhere
       | else, but there are plenty of hoops. The CLOS truly feels like a
       | superpower. That said, there is a huge dearth of libraries. So in
       | that sense, there's usually _lots_ of hoops to jump through to
       | write an app. It 's just I like jumping through them because I
       | like writing code as a hobby. So fewer limitations, more hoops
       | (supporting libraries I feel the need to write).
       | 
       | > has lower "friction" between my thoughts and my program,
       | 
       | Unfortunately I often think in Python or Bash because those are
       | my day job languages, so there's often friction between how I
       | think and what I need to write. Also AI is allegedly bad at lisp
       | due to reduced training corpus. Copilot works, sorta.
       | 
       | > is easily customizable,
       | 
       | Yup, that's its defining feature. Easy to add to the language
       | with macros. This can be very bad, but also very good, depending
       | on its use. It can be very worth it both to implementer and user
       | to add to the language as part of a library if documented well
       | and done right, or it can make code hard to read or use. It must
       | be used with care.
       | 
       | > and, frankly, more fun.
       | 
       | This is the true reason I actually use Lisp. I don't know why. I
       | think it's because it's really fun to write it. There are no
       | limitations. It's super expressive. The article goes into the
       | substitution principle, and this makes it easy to refactor. It
       | just feels good having a REPL that makes it easy to try new ideas
       | and a syntax that makes refactoring a piece of cake. The Lisp
       | Discord[1] has some of the best programmers on the planet in it,
       | all easy to talk to, with many channels spanning a wide range of
       | programming interests. It just feels good to do lisp.
       | 
       | 1: https://discord.gg/HsxkkvQ
        
       | DadBase wrote:
       | Had a PalmPilot taped to a modem that did our auth. Lisp made the
       | glue code feel like play. No types barking, no ceremony--just
       | `(lambda (x) (tinker x))`. We didn't debug, we conversed. Swapped
       | thoughts with the REPL like it was an old friend.
        
         | no_wizard wrote:
         | Though these are minor complaints, there is a couple things I'd
         | like to change about a Lisp language.
         | 
         | One is its the implicit function calls. For example, you'll
         | usually see calls like this: `(+ 1 2)` which translates to 1 +
         | 2, but I would find it more clear if it was `(+(1,2))` where
         | you have a certain explicitness to it.
         | 
         | It doesn't stop me from using Lisp languages (Racket is fun,
         | and I been investigating Clojure) but it took way too long for
         | the implicit function stuff to grok in my brain.
         | 
         | My other complain is how the character `'` can have overloaded
         | meaning, though I'm not entirely sure if this is implementation
         | dependent or not
        
           | DadBase wrote:
           | First time I saw (+ 1 2), I thought it was a typo. Spent an
           | hour trying to "fix" it into (1 + 2). My professor let me.
           | Then he pointed at the REPL and said, "That's not math--it's
           | music." Never forgot that. The '? That's the silent note.
        
             | no_wizard wrote:
             | It's due to Polish notation[0] as far as I understand it.
             | This is how that notation for mathematics works.
             | 
             | I suppose my suggestion would break those semantics.
             | 
             | [0]: https://en.wikipedia.org/wiki/Polish_notation
        
               | DadBase wrote:
               | Aye, Polish notation sure. But what he gave me wasn't a
               | lecture, it was a spell.
               | 
               | Syntax mattered less than rhythm. Parens weren't fences,
               | they were measures. The REPL didn't care if I understood.
               | It played anyway.
        
           | wdkrnls wrote:
           | R works exactly as you describe. You can type `+`(1, 2) and
           | get 3 because in R everything that happens is a function call
           | even if a few binary functions get special sugar so you can
           | type 1 + 2 for them as well. The user can of course make
           | their own of these if they wrap them in precents. For
           | example: `%plus%` = function(a, b) { `+`(a, b)}. A few
           | computer algebra systems languages provide even more
           | expressivity like yacas and fricas. The later even has a type
           | system.
        
             | seanw444 wrote:
             | Similar in Nim as well.
        
               | ckmate-king-2 wrote:
               | In Standard ML, you can do either 1+2 or op+(1, 2).
        
           | eyelidlessness wrote:
           | How is it implicit? The open parenthesis is before the
           | function name rather than after, but the function isn't
           | called without both parentheses.
           | 
           | If you want to use commas, you can in Lisp dialects I'm
           | familiar with--they're optional because they're treated as
           | whitespace, but nothing is stopping you if you find them more
           | readable!
        
             | apgwoz wrote:
             | , is typically "unquote." Clojure is the only "mainstream"
             | Lisp that allows , as whitespace. Has meaning in CL and
             | Scheme.
        
               | kazinator wrote:
               | Ancient LISP (caps deliberate) in fact had optional
               | commas that were treated as whitespace. You can see this
               | in the Lisp 1 Programmer's Manual (dated 1960).
               | 
               | This practice quickly disappeared though. (I don't have
               | an exact time line about this.)
        
               | eyelidlessness wrote:
               | My mistake!
               | 
               | That's what I get for not double checking... well...
               | basically _anything_ I think during my first cup of
               | coffee.
        
           | MarceColl wrote:
           | It's not really implicit though, the first element of a list
           | that is evaluated is always a function. So (FUN 1 2) is an
           | explicit function call. The problem is that it doesn't look
           | like C-like languages, not that it's not explicit.
           | 
           | In theory ' just means QUOTE, it should not be overloaded
           | (although I've mostly done Common Lisp, so no idea if in
           | other impl that changes). Can you show an example of
           | overloaded meaning?
        
             | no_wizard wrote:
             | There's an example I saw where `'` was used as a way to
             | denote a symbol, but I can't find that explicit example. It
             | wasn't SBCL, I believe it may have been Clojure. Its
             | possible I'm misremembering.
             | 
             | That said, since I work in C-like languages during the day,
             | I suppose my minor complaint has to do with ease of
             | transition, it always takes me a minute to get acquainted
             | to Lisp syntax and read Lisp code any time I work with it.
             | 
             | Its really a minor complaint and one I probably wouldn't
             | have if I worked with a Lisp language all day.
        
               | MarceColl wrote:
               | that's correct, but its not that it denotes a symbol as a
               | different function of ', its the same function. Turn code
               | into data. That's all symbols are (in CL at least).
               | 
               | For example in a quoted list, you dont need to quote the
               | symbols because they are already in a quoted expression!
               | 
               | '(hello hola)
               | 
               | ' really just says "do not evaluate whats next, treat it
               | as data"
        
               | MarceColl wrote:
               | to be a bit more precise, everytime you have a name in
               | common lisp that is already a symbol. But it will get
               | evaluated. If its as the first item of an evaluated list
               | it will be looked at in the function name space, if
               | elsewhere it will be looked up at the variable name
               | space. What quote is doing is just asking the compiler
               | not to evaluate it and treat it as data.
        
               | codr7 wrote:
               | Symbols are quoted representations of identifiers so it
               | still does the same thing.
        
           | bcrosby95 wrote:
           | func(a, b) is basically the same as (func a b). You're just
           | moving the parens around. '+' is extra 'strange' because in
           | most languages it isn't used like other functions: imagine if
           | you had to write +(1, 2) in every C-like.
        
           | panzagl wrote:
           | First person to ask for more parenthesis in Lisp.
        
           | zelphirkalt wrote:
           | Surely you mean (+ (, 1 2))
           | 
           | ;)
        
           | Reefersleep wrote:
           | It's not implicit in this case, it's explicit. + is the
           | function you're calling. And there's power in having
           | mathematical operations be functions that you can manipulate
           | and compose like all other functions, instead of some special
           | case of infix implicit (to me, yeah) function calling, like 1
           | + 2, where it's no longer similar to other functions.
        
         | owlstuffing wrote:
         | > No types barking
         | 
         | No thanks
        
           | codr7 wrote:
           | Common Lisp supports gradual typing and will (from my
           | experience) do a much better job of analyzing code and
           | pointing out errors than your typical scripting language.
        
           | tmtvl wrote:
           | The reason I switched from Scheme to Common Lisp was because
           | I could say...                 (defun foo (x)
           | (declare (type (Integer 0 100) x))         (* x
           | (get-some-value-from-somewhere-else)))
           | 
           | And then do a (describe 'foo) in the REPL to get Lisp to tell
           | me that it wants an integer from 0 to 100.
        
       | shadowgovt wrote:
       | The most impressive thing, to me, about LISP is how the very,
       | very small distance between the abstract syntax tree and the
       | textual representation of the program allows for some very
       | powerful extensions to the language with relatively little
       | change.
       | 
       | Take default values for function arguments. In most languages,
       | that's a careful consideration of the nuances of the parser, how
       | the various symbols nest and prioritize, whether a given symbol
       | might have been co-opted for another purpose... In LISP, it's
       | "You know how you can have a list of symbols that are the
       | arguments for the function? Some of those symbols can be lists
       | now, and if they are, the first element is the symbolic argument
       | name and the second element is a default value."
        
       | i_love_retros wrote:
       | Given that code is mostly written by LLMs now (or will be soon)
       | isn't it better to just use the best language that fits these
       | requirements:
       | 
       | - LLM well trained on it. - Easy for human team to review. -
       | Meets performance requirements.
       | 
       | Prob not lisp?
        
         | elllg wrote:
         | how is that anywhere close to a given?
        
         | worthless-trash wrote:
         | Llm content is trained in popularity. If we use that as a
         | metric there will never be any improvements or changes again.
        
       | discmonkey wrote:
       | Good article. Funnily enough the throw away line "I don't see
       | parentheses anymore". Is my greatest deterrent with lisp. It's
       | not the parens persay, it's the fact that I'm used to reading up
       | to down and left to right. Lisp without something like the
       | clojure macro ->, means that I am reading from right to left,
       | bottom to top - from inside out.
       | 
       | If i programmed enough in lisp I think my brain would adjust to
       | this, but it's almost like I can't full appreciate the language
       | because it reads in the "wrong order".
        
         | eadmund wrote:
         | > It's not the parens persay, it's the fact that I'm used to
         | reading up to down and left to right. Lisp without something
         | like the clojure macro ->, means that I am reading from right
         | to left, bottom to top - from inside out.
         | 
         | I'm not certain how true that really is. This:
         | foo(bar(x), quux(y), z);
         | 
         | looks pretty much identical to:                   (foo (bar x)
         | (quux y) z)
         | 
         | And of course if you want to assign them all to variables:
         | int bar_x = bar(x);         char quux_y = quux(y);
         | return foo(bar_x, quux_y, z);
         | 
         | is pretty much the same as:                   (let ((bar-x (bar
         | x))               (quux-y (quux y)))           (foo bar-x
         | quux-y z))
         | 
         | FWIW, 'per se' comes from the Latin for 'by itself.'
        
           | kibwen wrote:
           | The tragedy of Lisp is that postfix-esque method notation
           | just plain looks better, especially for people with the
           | expectation of reading left-to-right.                   let
           | bar_x = x.bar()         let quux_y = y.quux()         return
           | (bar_x, quux_y, z).foo()
        
             | tmtvl wrote:
             | De gustibus non disputandum est, I personally find the
             | C++/Java/Rust/... style postfix notation (foo.bar()) to be
             | appalling.
        
               | kazinator wrote:
               | TXR Lisp has this notation, combined with Lisp parethesis
               | placement.
               | 
               | Tather than obj.f(a, b). we have obj.(f a b).
               | 1> (defstruct dog ()            (:method bark (self)
               | (put-line "Woof!")))       #<struct-type dog>       2>
               | (let ((d (new dog)))            d.(bark))       Woof!
               | t
               | 
               | The dot notation is more restricted than in mainstream
               | languages, and has a strict correspondence to underlying
               | Lisp syntax, with read-print consistency.
               | 3> '(qref a b c (d) e f)       a.b.c.(d).e.f
               | 
               | Cannot have a number in there; that won't go to dot
               | notation:                 4> '(qref a b 3 (d) e f)
               | (qref a b 3 (d)         e f)
               | 
               | Chains of dot method calls work, by the way:
               | 1> (defstruct circular ()            val
               | (:method next (self) self))       #<struct-type circular>
               | 2> (new circular val 42)       #S(circular val 42)
               | 3> *2.(next).(next).(next).(next).val       42
               | 
               | There must not be whitespace around the dot, though; you
               | simply canot split this across lines. In other words:
               | *2.(next)        .(next) ;; nope!        .(next) ;; what
               | did I say?
               | 
               | The "null safe" dot is .? The following check _obj_ for
               | _nil_ ; if so, they yield _nil_ rather than trying to
               | access the object or call a method:
               | obj.?slot       obj.?(method arg ...)
        
             | ssivark wrote:
             | And what about when `bar` takes several inputs? Postfix
             | seems like an ugly hack that hyper-fixates on functions of
             | a single argument to the detriment of everything else.
        
             | wk_end wrote:
             | Looks better is subjective, but it has its advantages both
             | for actual autocomplete - as soon as I hit the dot key my
             | IDE can tell me the useful operations for the obejct - and
             | also for "mental autocomplete" - I know exactly where to
             | look to find useful operations on the particular object
             | because they're organized "underneath" it in the conceptual
             | hierarchy. In Lisps (or other languages/codebases that
             | aren't structured in a non-OOP-ish way) this is often a
             | pain point for me, especially when I'm first trying to make
             | my way into some code/library.
             | 
             | As a bit of a digression:
             | 
             | The ML languages, as with most things, get this (mostly)
             | right, in that by convention types are encapsulated in
             | modules that know how to operate on them - although I can't
             | help but think there ought to be more than convention
             | enforcing that, at the language level.
             | 
             | There is the problem that it's unclear - if you can
             | Frobnicate a Foo and a Baz together to make a Bar, is that
             | an operation on Foos, on Bazes, or on Bars? Or maybe you
             | want a separate Frobnicator to do it? (Pure) OOP languages
             | force you to make an arbitrary choice, Lisp and co. just
             | kind of shrug, the ML languages let you take your take your
             | pick, for better or worse.
        
               | CyberDildonics wrote:
               | It's not really subjective because people have had the
               | opportunity to program in the nested 'read from the
               | inside out' style of lisp for 50 years and almost no one
               | does it.
        
               | no_wizard wrote:
               | I think the cost of Lisp machines was the determining
               | factor. Had it been ported to more operating systems
               | earlier history could be different right now.
        
           | all2 wrote:
           | The lisp is harder to read, for me. The first double paren is
           | confusing.                   (let (bar-x (bar x))
           | (quux-y (quux y)))         (foo bar-x quux-y z)
           | 
           | Why is the second set of parens necessary?
           | 
           | The nesting makes sense to an interpreter, I'm sure, but it
           | doesn't make sense to me.
           | 
           | Is each top-level set of parens a 'statement' that executes?
           | Or does everything have to be embedded in a single list?
           | 
           | This is all semantics, but for my python-addled brain these
           | are the things I get stuck on.
        
             | jasbrg wrote:
             | > Why is the second set of parens necessary?
             | 
             | it distinguishes the bindings from the body.
             | 
             | strictly speaking there's a more direct translation using
             | `setq` which is more analogous to variable assignment in
             | C/Python than the `let` binding, but `let` is idiomatic in
             | lisps and closures in C/Python aren't really distinguished
             | from functions.
        
               | eadmund wrote:
               | You're right!                   (let (bar-x quux-y)
               | (setq bar-x (bar-x)                 quux-y (quux y))
               | (foo bar-x quux-y z))
               | 
               | I just wouldn't normally write it that way.
        
             | MayeulC wrote:
             | I am not a Lisp expert by any stretch, but let's clarify a
             | few things:
             | 
             | 1. Just for the sake of other readers, we agree that the
             | code you quoted does not compile, right?
             | 
             | 2. `let` is analogous to a scope in other languages (an
             | extra set of {} in C), I like using it to keep my variables
             | in the local scope.
             | 
             | 3. `let` is structured much like other function calls. Here
             | the first argument is a list of assignments, hence the
             | first double parenthesis (you can declare without
             | assigning,in which case the double parenthesis disappears
             | since it's a list of variables, or `(variable value)`
             | pairs).
             | 
             | 4. The rest of the `let` arguments can be seen as the body
             | of the scope, you can put any number of statements there.
             | Usually these are function calls, so (func args) and it is
             | parenthesis time again.
             | 
             | I get that the parenthesis can get confusing, especially at
             | first. One adjusts quickly though, using proper indentation
             | helps.
             | 
             | I mostly know lisp trough guix, and... SKILL, which is a
             | proprietary derivative from Cadence, they added a few
             | things like inline math, SI suffixes (I like that one),
             | and... C "calling convention", which I just find weird: the
             | compiler interprets foo(something) as (foo something). As I
             | understand it, this just moves the opening parenthesis
             | before the preceding word prior to evaluation, if there is
             | no space before it.
             | 
             | I don't particularly like it, as that messes with my C
             | instincts, respectively when it comes to spotting the
             | scope. I find the syntax more convoluted with it, so harder
             | to parse (not everything is a function, so parenthesis
             | placement becomes arbitrary):                   let(
             | (bar-x(bar(x))              quux-y(quux(y)))
             | foo(bar-x quux-y z)         )
        
             | krferriter wrote:
             | The code is written the same way it is logically
             | structured. `let` takes 1+ arguments: a set of symbol
             | bindings to values, and 0 or more additional statements
             | which can use those symbols. In the example you are
             | replying to, `bar-x` and `quux-y` are symbols whose values
             | are set to the result of `(bar x)` and `(quux y)`. After
             | the binding statement, additional statements can follow. If
             | the bindings aren't kept together in a `[]` or `()` you
             | can't tell them apart from the code within the `let`.
        
             | kazinator wrote:
             | The _let_ construct in Common Lisp and Scheme supports
             | imperative programming, meaning that you have this:
             | (let variable-bindings statment1 statement2 ... statementN)
             | 
             | If statementN is reached and evaluates to completion, then
             | its value(s) will be the result value(s) of _let_.
             | 
             | The _variable-bindings_ occupy one argument position in
             | _let_. This argument position has to be a list, so we can
             | have multiple variables:                 (let (...) ...)
             | 
             | Within the list we have about two design choices: just
             | interleave the variables and their initializing
             | expressions:                 (let (var1 value1
             | var2 value2             var3 value3)         ...)
             | 
             | Or pair them together:                 (let ((var1 value1)
             | (var2 value2)             (var3 value3)         ...)
             | 
             | There is some value in pairing them together in that if
             | something is missing, you know what. Like where is the
             | error here?                 (let (a b c d e) ...)
             | 
             | we can't tell at a glance which variable is missing its
             | initializer.
             | 
             | Another aspect to this is that Common Lisp allows a
             | variable binding to be expressed in three ways:
             | var       (var)       (var init-form)
             | 
             | For instance                 (let (i j k (l) (m 9)) ...)
             | 
             | binds i, j and k to an initial value of _nil_ , and m to 9.
             | 
             | Interleaved vars and initforms would make initforms
             | mandatory. Which is not a bad thing.
             | 
             | Now suppose we have a form of _let_ which evaluates only
             | one expression (let variable-bindings expr), which is
             | mandatory. Then there is no ambiguity; we know that the
             | last item is the expr, and everything before that is
             | variables. We can contemplate the following syntax:
             | (let a 2 b 3 (+ a b)) -> 5
             | 
             | This is doable with a macro. If you would prefer to write
             | your Lisp code like this, you can have that today and never
             | look back. (Just don't call it _let_ ; pick another name
             | like _le_!)
             | 
             | If I have to work with your code, I will grok that
             | instantly and not have any problems.
             | 
             | In the wild, I've seen a let1 macro which binds one
             | variable:                 (let1 var init-form statement1
             | statement2 ... statementn)
        
         | whalesalad wrote:
         | This is the most elementary hurdle a lisp programmer will face.
         | You do indeed become adjusted to it quite quickly. I wouldn't
         | let this deter you from exploring something like Clojure more
         | deeply.
        
         | MarceColl wrote:
         | I think it really depends, in Common Lisp for example I don't
         | think that's the case:                 (progn         (do-
         | something)         (do-something-else)         (do-a-third-
         | thing))
         | 
         | The only case where it's a bit different and took some time for
         | me to adjust was that adding bindings adds an indent level.
         | (let ((a 12)             (b 14))         (do-something a)
         | (do-something-else b)         (setf b (do-third-thing a b)))
         | 
         | It's still mostly top-bottom, left to right. Clojure is quite a
         | bit different, but it's not a property of lisps itself I'd say.
         | I have a hard time coming up with examples usually so I'm open
         | to examples of being wrong here.
        
           | fc417fc802 wrote:
           | Your example isn't a very functional code style though so I
           | don't know that I'd consider it to be idiomatic. Generally
           | code written in a functional style ends up indented many
           | layers deep. Below is a quick (and quite tame) example from
           | one of the introductory guides for Racket. My code often ends
           | up much deeper. Consider what it would look like if one of
           | the cond branches contained a nested cond.
           | (define (start request)         (define a-blog
           | (cond [(can-parse-post? (request-bindings request))
           | (cons (parse-post (request-bindings request))
           | BLOG)]                 [else                  BLOG]))
           | (render-blog-page a-blog request))
           | 
           | https://docs.racket-lang.org/continue/index.html
        
             | MarceColl wrote:
             | Common Lisp, which is what I use, is not really a
             | functional oriented language. I'd say the above is okay in
             | CL.
        
               | fc417fc802 wrote:
               | I must have missed that memo. Sure it's remarkably
               | flexible and simultaneously accommodates other
               | approaches, but most of the code I see in the wild leans
               | fairly heavily into a functional style. I posted a CL
               | link in an adjacent comment.
               | 
               | Here's an example that mixes in a decent amount of
               | procedural code that I'd consider idiomatic.
               | https://github.com/ghollisjr/cl-ana/blob/master/hdf-
               | table/hd...
        
         | buttercraft wrote:
         | > reading from right to left, bottom to top - from inside out
         | 
         | I don't understand why you think this. Can you give an example?
        
           | fc417fc802 wrote:
           | Does this example help? https://github.com/ghollisjr/cl-
           | ana/blob/master/binary-tree/...
        
         | Uhhrrr wrote:
         | per se
        
       | bgitarts wrote:
       | Always read from experienced developers praising lisps, but why
       | is it so rare in production applications?
        
         | geor9e wrote:
         | People smart enough to read and write it are rare.
        
           | Capricorn2481 wrote:
           | Is it about intelligence or just not being used to/having
           | time for learning a different paradigm?
           | 
           | I personally have used LISP a lot. It was a little rough at
           | first, but I got it. Despite having used a lot of languages,
           | it felt like learning programming again.
           | 
           | I don't think there's something special about me that allowed
           | me to grok it. And if that were the case, that's a horrible
           | quality in a language. They're not supposed to be difficult
           | to use.
        
         | gpcz wrote:
         | Looking for a nice, solid, well-documented library to do
         | something is difficult for most stuff. There are some real gems
         | out there, but usually you end up having to roll your own
         | thing. And Lisp generally encourages rolling your own thing.
        
         | mikedelago wrote:
         | In my case, my boss won't let me.
        
         | grandempire wrote:
         | JavaScript and Python have adopted almost every feature that
         | differentiated Lisp from other languages. So in comparison Lisp
         | is just more academic, esoteric, and advanced.
        
       | jll29 wrote:
       | As much as I sympathize with this post and similar ones, and as
       | much I personally like functional thinking, LISP environments are
       | not nearly as advanced anymore as they used to be.
       | 
       | Which Common LISP or Scheme environment (that runs on, say Ubuntu
       | Linux on a typical machine from today) gets even close to the
       | past's LISP machines, for example? And which could compete with
       | IntelliJ IDEA or PyCharm or Microsoft Code?
       | 
       | https://ssw.jku.at/General/Staff/PF/genera-screenshots.html
        
         | vindarel wrote:
         | Common Lisp can compete with Python no problem, that's what
         | matters to me. You get:
         | 
         | - truly interactive development (never wait for something to
         | restart, resume bugs from any stack frame after you fixed
         | them),
         | 
         | - self-contained binaries (easy deployment, my web app with all
         | the dependencies, HTML and CSS is +-35MB)
         | 
         | - useful compile-time warnings and errors, a keystroke away,
         | for Haskell levels see Coalton (so better than Python),
         | 
         | - fast programs compiled to machine code,
         | 
         | - no GIL
         | 
         | - connect to, inspect or update running programs (Slime/Swank),
         | 
         | - good debugging tools (interactive debugger, trace, stepper,
         | watcher (on some impls)...)
         | 
         | - stable language and libraries (although the implementations
         | improve),
         | 
         | - CLOS and MOP,
         | 
         | - etc
         | 
         | - good editor support: Emacs, Vim, Atom/Pulsar (SLIMA), VScode
         | (ALIVE), Jetbrains (SLT), Jupyter kernel, Lem, and more:
         | https://lispcookbook.github.io/cl-cookbook/editor-support.ht...
         | 
         | What we might not get:
         | 
         | - advanced refactoring tools -also because we need them less,
         | thanks to the REPL and language features (macros, multiple
         | return values...).
         | 
         | ---
         | 
         | For a lisp machine of yesterday running on Ubuntu or the
         | browser: https://interlisp.org/
        
       | submeta wrote:
       | > Lisp's dreaded Cambridge Polish notation is uniform and
       | universal. I don't have to remember whether a form takes curly
       | braces or square brackets or what the operator precedency is or
       | some weird punctuated syntax that was invented for no good
       | reason. It is (operator operands ...) for everything. Nothing to
       | remember. I basically stopped noticing the parenthesis 40 years
       | ago. I can indent how I please.
       | 
       | Well, that might be true for Scheme, but not for CL. There are
       | endless forms for loops. I will never remember all of them. Or
       | even a fraction of it. Going through Guy Steel's CL book, I tend
       | to think that I have a hard time remembering most of the forms,
       | functions, and their signatures.
        
       ___________________________________________________________________
       (page generated 2025-04-11 23:00 UTC)