[HN Gopher] Hell Is Other REPLs
       ___________________________________________________________________
        
       Hell Is Other REPLs
        
       Author : ducktective
       Score  : 234 points
       Date   : 2021-08-29 10:25 UTC (12 hours ago)
        
 (HTM) web link (hyperthings.garden)
 (TXT) w3m dump (hyperthings.garden)
        
       | amelius wrote:
       | > (...) a single "revolutionary" concept or idea. Some examples
       | include safety in Rust (...)
       | 
       | I think these languages are focusing too much on a few ideas
       | while ignoring the rest. For example if I'm writing a Rust
       | program which launches missiles, then I can still accidentally
       | launch these missiles in safe code. A GC'd language would still
       | provide good memory safety guarantees while allowing me to focus
       | more on the problem at hand (not distracting me with type
       | errors), and thus it could be safer overall.
        
         | kazoomonger wrote:
         | I'll admit that I like Rust, but this seems like an odd take to
         | me. "Not distracting me with type errors" seems directly at
         | odds with "safer overall", especially for a missile launching
         | system. That sounds like a recipe for "Whoops, there goes New
         | York, but at least the code looked nice".
        
           | amelius wrote:
           | The catch here is that not all type errors are errors. I'm
           | not arguing here against a typesystem, just against overly
           | strict typesystems.
        
             | kazoomonger wrote:
             | I would interested in seeing an example of what sort of
             | type errors you mean. IME, the Rust compiler does a great
             | job of catching actual mistakes in the type system, such as
             | with Send/Sync. It also pretty easily lets you tell the
             | compiler "no, I know what I'm doing" and say "unsafe impl
             | Send for Foo {}" to do it anyways.
        
               | amelius wrote:
               | I'm talking about the sort of type errors you get when
               | you try to implement a doubly linked list in safe Rust.
               | 
               | https://rust-unofficial.github.io/too-many-lists/
        
             | chakkepolja wrote:
             | Rust typesystem has its place in many soft realtime /
             | system programming domains.
             | 
             | Problem is people trying to push it as one true solution.
             | But that's a human problem.
        
       | marciol wrote:
       | Ruby pry brings a lot of those conveniences that I think really
       | hard to live without. There is a presentation[1] when Conrad
       | Irwin shows what is possible.
       | 
       | [1]: https://youtu.be/D9j_Mf91M0I?t=1238
        
       | Y_Y wrote:
       | It would be nice to get an example of how this wonderful REPL
       | does all the cool things mentioned.
       | 
       | In fact Haskell has a decent REPL, and you can (if you really
       | want to) explicitly allow IO outside the IO Monad, or skip the
       | type checker, though I find them more of a moral compass than an
       | impressive dictat. I don't know if you can crash straight to the
       | debugger like in common lisp though.
        
         | CraigJPerry wrote:
         | A cool example: https://youtu.be/3HxVMGaiZbc?t=1783
         | 
         | But better to just block 5 mins in your calendar and try it:
         | https://github.com/PEZ/rn-rf-shadow
         | 
         | Don't play with this if you like hot-reload or hot-refresh
         | features in react or jrebel or whatever. You will forever see
         | these other approaches as lame in comparison... speaking from
         | someone who was previously very happily ignorant, living my
         | best life on the JRebel side of the fence.
        
         | throwaway73847 wrote:
         | Don't let the 2005 date fool you.
         | https://yewtu.be/watch?v=_B_4vhsmRRI
        
         | Tarean wrote:
         | You can run your program in the repl and :set -fbreak-on-error
         | .
         | 
         | You can get stack traces with access to variables via :set
         | trace, but because of optimizations and lazy evaluation they
         | are similarly jumbled as async code debugging.
         | 
         | Haskell has some nice advantages like `:r` for reloading,
         | similarly you can reload a dynamically linked binary and the
         | old version is only unlinked after all references to it are
         | gone. There is a significant difference to dynamic languages,
         | though, because having different versions in memory leading to
         | incompatible types would be a lot more common with a partial
         | reloading workflow.
        
         | zetalyrae wrote:
         | Nicolas Hafner[0], a prolific Common Lisp open-source
         | developer, has been streaming the development of his adventure
         | platformer Kandria[1] on YouTube:
         | https://www.youtube.com/watch?v=DjjT7Fjh0Kk&list=PLkDl6Irujx...
         | 
         | It's pretty cool to see music, graphics, and game behaviour
         | change live in response to code entered into the REPL.
         | 
         | [0]: https://shinmera.com/
         | 
         | [1]: https://github.com/Shinmera/kandria
        
       | skohan wrote:
       | > These "big idea languages" tend to assert a kind of
       | "programming ideology" over reality, and tend to, in my opinion,
       | distance you from the thing you are trying to do, often, they
       | claim, "for your own good" ... "You want to give HOW many objects
       | a mutable reference to this value?" complains the Rust compiler.
       | "Are you insane? You are a Rustacean, you don't do that kind of
       | thing!"
       | 
       | I agree with the spirit of this line of thought: in general I
       | favor pragmatism over zealotry, and I think it should be up to
       | the programmer to determine how a tool should be used, not the
       | tool itself.
       | 
       | However when we talk about something like the safety constraints
       | imposed by Rust, it's more about citizenship than it is about the
       | compiler being overly opinionated. By baking those constraints
       | into the language, I can have confidence when I use third party
       | code that it reaches at least some standard in terms of memory
       | safety and thread safety. Much in the same way as a strict type
       | system, those constraints free me up to not have to think about
       | certain types of issues.
       | 
       | If I am writing code which only I will consume, I'm also free to
       | use escape hatches to sidestep these constraints when I know
       | they're not relevant.
       | 
       | I've written tens of thousands of lines of Rust by now, and I'm
       | still not convinced the Rust model is the right one in terms of
       | achieving its goals, but I think the approach is "reality based"
       | with respect to avoiding some of the problems which can occur
       | while developing software in collaboration with others.
        
         | chousuke wrote:
         | In my view it's very pragmatic to leave dealing with ownership
         | to the compiler. It's something that you _have_ to deal with in
         | any language (even if you have a GC) if you want to write
         | correct code, but it 's not something most programmers truly
         | prioritize most of the time. Even those who do think about
         | ownership can't get it right 100% of the time because they're
         | human and get tired. Keeping track of any kind of state is a
         | constant drain on mental resources.
         | 
         | Therefore, having the compiler deal with ownership correctly
         | most of the time and _maybe_ get in my way a bit some of the
         | time when it 's actually wrong (most of the time it isn't) is a
         | tradeoff I'm happy to make.
        
           | codeflo wrote:
           | I agree. The important thing for the other cases is to be
           | pragmatic and use the escape hatches. Use a Mutex even though
           | it's not necessary and accept the runtime cost, or use an
           | unsafe pointer, but get the job done. I've seen the
           | phenomenon in both Haskell and Rust now where people would
           | introduce way to much complexity for a relatively simple
           | problem to conform to some sort of ideology.
        
           | skohan wrote:
           | I agree. The point where I'm not totally convinced on Rust's
           | model is when it comes to lifetime semantics.
           | 
           | People talk about the problems colored functions when it
           | comes to async, but in my experience the real issue you run
           | into with Rust is that once you need to introduce an explicit
           | lifetime into a data structure, the complexity of everything
           | touching it starts to balloon out of control.
           | 
           | This creates all kinds of complications, creates problems
           | with automatically deriving the future trait, and leads to
           | workarounds where people end up passing id's around
           | everywhere instead of references in a lot of cases, which can
           | effectively be a way of circumventing the borrow checker.
           | 
           | I don't know what the answer is, but sometimes Rust feels
           | like it's an abstraction or two away from really solving the
           | problems it wants to solve in an elegant way.
        
             | mst wrote:
             | > I don't know what the answer is, but sometimes Rust feels
             | like it's an abstraction or two away from really solving
             | the problems it wants to solve in an elegant way.
             | 
             | I feel like it's not unreasonable to say that some parts of
             | rust are impressive because they make hard things
             | ergonomic, and others - so far - are mostly impressive
             | because they make really hard things possible at all.
             | 
             | Or: I share your intuition, but assuming there even -is- an
             | answer, I think at the very least rust has provided a great
             | service in figuring out what the right questions are.
        
           | zetalyrae wrote:
           | For languages that have explicit memory management and
           | concurrency (e.g. Rust) having compiler tracking of
           | ownership, or equivalent, is an absolute necessity in the
           | modern world.
           | 
           |  _But_ I 'd argue most languages don't need ownership. GC is
           | fine. Most of the problems we deal with in commercial
           | software development are succinctly expressed in GC'd
           | languages, and the benefit of using a language that
           | explicitly tracks ownership is greater performance from being
           | able to be closer to the metal.
        
             | chousuke wrote:
             | You need ownership tracking even in languages with GC
             | because you will be dealing with resources that are more
             | than just memory, and with those GC isn't really enough. A
             | GC alone doesn't really help you deal with concurrent
             | access, either. If you don't have a borrow checker, you
             | will be essentially doing the same work manually as you're
             | reading and writing code.
             | 
             | Rust's ownership tracking is not only about memory safety.
             | It can also help you with other kinds of resources whose
             | ownership you can encode using the type system.
        
               | masklinn wrote:
               | Yes indeed, I'd like it if other languages had ownership
               | and borrowing. Hell, I'd like it if a language could
               | actually achieve _linear types_ without holes or being
               | completely unusable.
               | 
               | They could allow more relaxed GC'd normal types, whether
               | default or opt-in, but affine and linear typing are
               | genuinely useful properties fo _reasoning about code_ ,
               | from both correctness and performance perspectives.
               | 
               | One needs to look no further than the string slicing
               | problem for that to be clear. It's not an issue in Rust,
               | but in every GC'd language you have to pick between:
               | 
               | 1. eagerly copying the slice, which incurs additional
               | often unnecessary CPU and memory costs, but avoids
               | 
               | 2. every substring operation which isn't defensively
               | copied being a potential memory leak as you're slicing 3
               | characters off of a 5MB string on every HTTP request and
               | storing that in a global map, which turns out to store
               | the entire 5MB string you got it from
               | 
               | Or some even more complicated magic where you only
               | perform the copy if the substring escapes, at which point
               | your performance and memory characteristics become wildly
               | unstable and the mere act of extracting a function can
               | bring the entire system to its knees.
        
               | zetalyrae wrote:
               | >Hell, I'd like it if a language could actually achieve
               | linear types without holes or being completely unusable.
               | 
               | I'm working on this: https://github.com/austral/austral/
               | 
               | The idea is to start with a simple, easily understood
               | linear type system and then add borrowing, but without
               | going too far in the direction where the type checker
               | becomes a tower of heuristics and conveniences so that
               | programmers can write normal-seeming code which magically
               | typechecks.
               | 
               | So you can learn how to write linear code from reading a
               | set of linearity rules, rather than from trial-and-error
               | against the linearity checker.
        
               | throwaway17_17 wrote:
               | A look at your anti-features list reminds me somewhat of
               | Zig. Obviously the polymorphism is not in the Zig
               | wheelhouse, but there are some similar seeming paths
               | running through the readme. Your comment had me expecting
               | a Haskel/ML presenting language, but now I'm wondering.
               | Where do you see the syntax going?
        
               | skohan wrote:
               | This seems like a very cool project!
               | 
               | Have you ever thought about putting a simple code example
               | right in the README? Maybe it's too early days, but I
               | know that's always the first thing I'm looking for when I
               | stumble across a new language.
        
               | zetalyrae wrote:
               | Yeah I should get around to doing that, but right now
               | there isn't much code that succinctly showcases the
               | linear typing features. Probably have to implement some
               | of the stdlib for that.
        
               | skohan wrote:
               | You need some way of managing ownership, but it doesn't
               | necessarily need to bubble up to the user level. For
               | instance Swift is going in the direction of leaning
               | heavily on value semantics, plus the actor model to wrap
               | concurrent data, to create a safe environment where the
               | user doesn't have to think about ownership at all.
               | 
               | Of course there's tradeoffs involved, but I think there
               | are multiple approaches to achieve safety and user-space
               | ownership constraints are only one of them.
        
               | pjmlp wrote:
               | Most GC languages invented before Java went mainstream
               | had value semantics as well actually, and this is one of
               | language defects that .NET improved over Java.
               | 
               | Many forget that MSIL is like LLVM, having all semantics
               | to compile C++, alongside GC.
               | 
               | Some of those capabilities weren't exposed to other .NET
               | languages, however since C# 7 they have been improving
               | it.
               | 
               | As of C#10, there is little left to do versus Modula-3 or
               | D.
        
               | pjmlp wrote:
               | Most GC languages have deterministic resource management,
               | not everything is a JavaScript like GC.
               | 
               | Borrow checker only helps dealing with concurrent access
               | on the very special case of multithread code accessing in
               | process data structures.
               | 
               | Anything else that follows outside of this, like
               | concurrent access to external resources, or via OS IPC,
               | there is little help.
               | 
               | https://doc.rust-lang.org/nomicon/races.html
        
       | [deleted]
        
       | [deleted]
        
       | tempodox wrote:
       | For those who like to cross a REPL with strong static typing and
       | don't want to go as far as Haskell, OCaml has a REPL and a tool
       | (ocamlmktop) that builds a REPL variant of your software as a
       | binary executable. Not quite as highly integrated as CommonLisp +
       | SLIME, but still very useful.
        
         | eatonphil wrote:
         | For Standard ML, SML/NJ and Poly/ML also have a very useful
         | REPL.
        
         | mdm12 wrote:
         | Another option for ML-variant fans is F#, which has a REPL as
         | well (built into many tools such as Rider or VS, or as a
         | standalone 'fsi' executable).
        
           | dharmaturtle wrote:
           | I've been using VS Code Notebooks with F# - it's fantastic.
           | Basically Jupyter Notebooks for VS Code.
           | 
           | Demo: https://youtu.be/_QnbV6CAWXc?t=1298
        
           | dunefox wrote:
           | I'm using F# + Rider at the moment as my language of choice
           | for implementing interpreters and compilers and I have to
           | say, I'm very pleased so far.
        
         | yawaramin wrote:
         | Also 'dune utop' which loads your library directly in the REPL,
         | which is quite nice for iterative development.
        
       | ekidd wrote:
       | > The Rust or Haskell compilers, for example, insist on policing
       | whatever private understanding you might have about the _meaning_
       | of your code.
       | 
       | The thing about "private understandings" is that they're just
       | another way of saying "we expect ceaseless, flawless vigilance"
       | (as one writer put it), not to mention "flawless communication
       | and training."
       | 
       | Languages which impose weird restrictions tend to do so because
       | it allows them to offer (nearly) ironclad guarantees about
       | something else.
       | 
       | There are certain programming idioms that work brilliantly in
       | Haskell. Those same idioms would be utterly miserable in the
       | presence of unrestricted mutation.
       | 
       | Or to take the author's other example, Rust forbids shared
       | mutable state, and it keeps careful track of ownership. But I can
       | freely use native CPU threads without worrying about anything
       | worse than a deadlock, and I can bang directly on raw bytes
       | without worrying about anything worse than a controlled runtime
       | failure. And this remains true even if team communication
       | occasionally fails or if someone makes a mistake.
       | 
       | Sometimes I want to rule out entire classes of potentially
       | dangerous mistakes, and not just have a "private understanding"
       | that nobody will ever make certain mistakes.
       | 
       | As always, it's a matter of using the right tool for the job. If
       | you need to write a high-performance, heavily-threaded network
       | server that parses malicious binary data, Rust is a great tool
       | _because_ of those restrictions. If you need to do highly
       | exploratory programming and invent new idioms to talk about your
       | problem domain, Common Lisp is awesome. And if you need to build
       | libraries where everything has a rigid mathematical structure,
       | Haskell is a great tool.
       | 
       | In my experience, Commony Lisp is a _deeply_ opinionated
       | language, and its most dramatic opinion is that  "your code and
       | your data should have identical representations and structures."
       | And for the right problem, that restriction is extremely
       | powerful.
        
         | aidenn0 wrote:
         | My data is often in a class, structure, or hash table, all of
         | which are vanishingly rare in my code...
        
           | throwaway17_17 wrote:
           | I'm curious, but my reading of your comment essentially boils
           | down to 'the actual data in my code is vanishingly rare'. Am
           | I reading that right? Is the comment actually saying that in
           | any given program most of the data is in some structure, but
           | compared to the rest of the code is it almost insignificant?
           | I'm not judging or contradicting, but if my reading is right,
           | that's the fist time I've heard anyone make that point in
           | such a straightforward manner.
        
             | erik_seaberg wrote:
             | Lisp has a lot of functions for working with lists, which
             | do double duty as functions for working with parsed code.
             | But optimized Lisp tends to use structs and arrays and
             | hashtables and avoids creating a lot of lists at runtime.
        
         | coldtea wrote:
         | > _Languages which impose weird restrictions tend to do so
         | because it allows them to offer (nearly) ironclad guarantees
         | about something else._
         | 
         | Yes, but oftentimes this is more like:
         | 
         | "If we tie you to this bed, you will never have a car accident!
         | You'll also never get lost! This also makes it easier to stay
         | healthy as we'll be feeding you the best food!"
         | 
         | Sure, but I'll also miss all kind of activities I could be
         | doing outside on my own. I'll also get bed sores. And I often
         | want a steak or ice cream, even if it's not ideal, but you
         | insist bringing me this organic gourmet vegan crap.
        
           | swsieber wrote:
           | I'd probably liken it traffic controls, like lanes w/lines,
           | stop-lights, stop-signs and other formalized rules of
           | driving.
           | 
           | Yeah, it might suck that you can get a ticket for treating a
           | red-light like a stop-sign at 6am in the middle of no-where.
           | Though this is me speaking from a "likes rust" perspective.
        
           | rtpg wrote:
           | Could you please describe a case where this is actually
           | happening in the languages people tend to imply this is
           | happening in?
           | 
           | Rust has unsafe, Haskell has unsafePerformIO. You might not
           | like how verbose some of the stuff is but all these languages
           | give you a foot gun if you really want it.
        
             | pjmlp wrote:
             | That is the usual cowboy programming sentiment that fueled
             | the same critic against Modula-2 and Pascal.
             | 
             | A well known article, written in bad faith, is "Why Pascal
             | is Not My Favorite Programming Language".
             | 
             | http://www.cs.virginia.edu/~evans/cs655/readings/bwk-on-
             | pasc...
             | 
             | Why bad faith? Most of the critic he makes against Pascal
             | was sorted out by Pascal dialects.
             | 
             | Which one can consider doesn't count as dialects aren't the
             | main language, yet by 1981, C was mostly available in K&R
             | dialects outside UNIX, like Small C.
             | 
             | And besides those Pascal dialects, Modula-2 standard was
             | released in 1978, while Mesa was powering XDE and Bravo at
             | Xerox.
        
           | bmn__ wrote:
           | Programming is for all kinds of people.
           | 
           | If you felt despair when you read the metaphor of
           | totalitarianism and just want to get away from languages that
           | want to grind an axe on you, and Lisp is not your cup of tea,
           | then Raku and Perl welcome you.
        
           | pjc50 wrote:
           | This is a purely emotive metaphor with no technical content?
        
             | coldtea wrote:
             | Yes, and as such perfectly isomorphic to the resistance the
             | programmer _feels_ when working e.g. to satisfy the borrow
             | checker or strict type systems.
             | 
             | This is not about some "technical" impossibility - language
             | still have an escape hatch like unsafe and "non-idiomatic"
             | ways of coding.
             | 
             | But to focus on the technical and ignore programmer
             | experience ("feelz") in that area, is like equating Idris
             | and Forth because "they're all turing complete and it all
             | ends up as binary code anyway".
        
           | tux3 wrote:
           | I think that point of view assumes bad faith.
           | 
           | No one sets out to create programming languages that tie
           | people to their beds. If people add restrictions, it's not to
           | make users miserable.
           | 
           | It's fine if you like being unrestricted, I like that too.
           | 
           | However, when you see people going out of their ways to add
           | barriers to their own work, you should assume they reap a
           | benefit that you're not fully appreciating, not that they
           | hate fun and want to outlaw ice cream.
        
             | coldtea wrote:
             | > _No one sets out to create programming languages that tie
             | people to their beds._
             | 
             | People can still end up designing such languages, even if
             | they didn't set out with that specific goal.
             | 
             | (Kind of like how you can bring war and division, even if
             | your goal is to bring some religious peace on Earth or "the
             | revolution").
             | 
             | > _If people add restrictions, it 's not to make users
             | miserable._
             | 
             | That's already explicit in my comment though. They didn't
             | tie the person to his bed to make him miserable but to
             | spare them from car accidents, to help them never get lost,
             | and other such niceties!
             | 
             | The misery is a by-product, not a design goal.
        
             | myfavoritedog wrote:
             | "Bad faith" is too strong a term, but if you've spent much
             | time in the Elm language sphere, you really will get what
             | the original poster is getting at. https://elm-lang.org/
        
             | AnimalMuppet wrote:
             | There's a reason they were called "bondage and discipline
             | languages". They created barriers for what they assumed to
             | be your own good. But as Edward Abbey said, "When I see
             | someone coming to do me good, I reach for my revolver."
             | That is, when someone wants to make decisions for my good,
             | they're deciding _for me_ ; that is, they're taking the
             | decision away from me, because they don't think I can be
             | trusted to choose the "right" thing. And then I'm supposed
             | to feel grateful for the help instead of resentful for
             | being treated like an idiot or a child!
             | 
             | They mean well. Fine. I still don't have to like it, and I
             | don't have to accept it.
        
             | throwaway17_17 wrote:
             | I don't think it takes bad faith to come to GP's
             | conclusion. Further, it implies a great deal of superiority
             | for you to assume that someone who does not like/enjoy the
             | restrictions of a language does not 'fully [appreciate]'
             | the supposed benefits of those restrictions. I, for one, do
             | not enjoy or value the restrictions (or underlying semantic
             | concept) of ownership in Rust. It just means that I don't
             | enjoy it, I understand the benefits which are claimed to
             | exist for adopting the Rust-centric view of programming. I
             | just don't think they are worthwhile for me. I'm not really
             | disagreeing with the point you make about language
             | designers intention, I doubt they set out to inflict misery
             | on users, but your assumption about not understanding some
             | alleged benefit is a bridge too far.
        
               | hackerfromthefu wrote:
               | The point is not whether they are worthwhile to you, but
               | to the problem space the program solves. Further that
               | includes the context of the organisation/developer that
               | will own it going forward.
        
           | pjmlp wrote:
           | It is more like, if you were an helmet, a seatbelt, metal
           | gloves, you will survive to tell the story.
        
         | MaxBarraclough wrote:
         | I don't have anything in particular to contribute, but it
         | struck me as interesting that the term _private understanding_
         | is used here. It reminds me of the subtitle of one of the more
         | well-known books on formal methods, _The B-Book: Assigning
         | Programs to Meanings._ [0]
         | 
         | Of course, using a formal methodology like B, _ceaseless,
         | flawless vigilance_ is mandatory.
         | 
         | [0] https://www.cambridge.org/gb/academic/subjects/computer-
         | scie...
        
         | marcosdumay wrote:
         | It's all about thinking frameworks. Ensuring that effects are
         | declared, that data has a static lifetime, or that your program
         | is rewritable data are tools you can use to focus your mind in
         | one part of the problem and less in another. Just like having
         | irestricted access to everything your computer can do, or data
         | + code encapsulation with multiple access levels.
         | 
         | Not recognizing this (like in the article) is a clear sign of a
         | single-paradigm developer that never really understood anything
         | else but is too arrogant (insecure maybe?) to assume he doesn't
         | understand something. So, the problem must be with everybody
         | else.
         | 
         | Anyway, a funny red-flag is that reaction to Haskell IO. People
         | that never really learned Haskell tend to complain about IO
         | with a deeper complaint of "why to I have to keep using
         | monads?", while after people learn it they tend to complain
         | about the lack of monad composition, with a deeper complaint of
         | "why can't I use monads for everything?"
        
           | arunix wrote:
           | Can you say more about the latter i.e. lack of monad
           | composition ("why can't I use monads for everything") ?
        
             | marcosdumay wrote:
             | After you learn how to solve states, input data, output
             | data, error handling, parsing context, resource mocking,
             | guaranteed resource release, and a lot of other things just
             | by declaring the correct monad when you call your code,
             | people tend to want to solve several of those at the same
             | time.
             | 
             | But for solving several of them, you need to compose your
             | monads, and there is no good general way to do that. There
             | are context dependent ways to compose some of them, but you
             | are always thinking about their compatibility and they
             | often require that you specialize your code.
        
               | a1369209993 wrote:
               | > But for solving several of them, you need to compose
               | your monads, and there is no good general way to do that.
               | 
               | It's (provably, I think) impossible for _fully_ general
               | monads, but for traversable-but-otherwise-general monads
               | you can do[0]:                 newtype (%) (f1::k1->Type)
               | (f2::k2->k1) (a::k2) = Comp { unComp :: (f1 (f2 a)) }
               | instance (Monad f1,Monad f2,Traversable f2) => Monad (f1
               | % f2) where         join (Comp a)  = Comp $ a >>= (map
               | join . mapA (unComp    ))         (Comp a) >>= k = Comp $
               | a >>= (map join . mapA (unComp . k))
               | 
               | (For that matter, you only need f2 to be traversable; f1
               | can be any monad at all.)
               | 
               | 0: copied from my own code, so it might need other
               | adjustment than s/map/fmap/ and s/mapA/traverse/.
        
         | nyanpasu64 wrote:
         | Rust does not "forbid shared mutable state". Instead it offers
         | both the usual usual shared references along with a new unique
         | reference, but restricts shared references so the type being
         | pointed to must opt into aliased mutation. Making sharing and
         | aliased mutation explicit in the type system allows both the
         | programmer and the compiler to reason about which variables can
         | change behind your back when calling functions or mutating
         | through other pointers, and which ones are referentially
         | transparent. (Though this property does come with sacrifices to
         | programmer convenience, namely the restrictions on Cell, the
         | runtime overhead and panic-proneness of RefCell, and the unsafe
         | blocks associated with UnsafeCell. Hopefully GhostCell can do
         | better.)
        
           | mst wrote:
           | Also, the article explicitly says that the rust compiler is
           | usually right, with - IMO - the implication that haskell is
           | likely usually right for its purposes too.
           | 
           | But people have to get mad about the one throwaway comment at
           | the top without even reading the next paragraph, let alone
           | the rest of the article :(
        
         | amelius wrote:
         | > As always, it's a matter of using the right tool for the job.
         | 
         | Nice in theory. In practice a manager will ask you to build A,
         | then later they will ask you to add B and C, and these
         | components should interact seamlessly of course. If you chose
         | your language based on A, then you might get stuck on B and C.
        
           | ekidd wrote:
           | Some of my favorite organizations have been the ones where
           | someone senior picked a very tiny number of "official"
           | languages early on, and established clear rules about when to
           | use which. Some examples:
           | 
           | - "Our web apps get written in Typescript."
           | 
           | - "Our data science gets written in Python."
           | 
           | - "Our inner loops get written as Rust CLI tools that contain
           | no business logic." (Or whatever. It depends on the problem
           | domain.)
           | 
           | This works because TypeScript, Python and Rust are all very
           | good at certain tasks, but they're each also fine general-
           | purpose languages with lots of third party libraries. If you
           | do somehow wind up writing a webserver in Python, you'll be
           | fine. Of course, something like Haskell is a deeper
           | commitment with more tradeoffs, and it's the wrong answer for
           | most organizations. (But if I were mathematically modeling
           | the pricing of complex derivative contracts, Haskell would
           | make the list.)
           | 
           | But this is why good senior people with taste are so useful,
           | and why competent technical management is worth its weight in
           | gold. Sometimes you want a toolbox with 2 or 3 well-chosen,
           | versatile tools. And sometimes you get organizations that try
           | to pound in nails with a screwdriver.
        
             | davedx wrote:
             | I was somewhat involved in this process at an org I worked
             | at, the thing is, some languages really are the best tool
             | for the job, and trying to build something in the wrong
             | language can really hurt software quality or reliability.
             | That said, I agree you should still _try to standardise_ to
             | a reasonable degree.
        
             | tharkun__ wrote:
             | It sort of makes sense. If things can still change. Have
             | you never been at a company where the above list was:
             | 
             | Our web apps get written in Perl (later added: with jQuery
             | for FE interactivity)
             | 
             | Our data science gets written in Oracle stored procedures
             | 
             | Our core business logic is written in Oracle stored
             | procedures!
             | 
             | Fat clients are written in Smalltalk
             | 
             | These were arguably even sort of well chosen at the time.
             | (I won't say which company this was as that would identify
             | me and I changed some details but this is a real life
             | example)
        
               | HelloNurse wrote:
               | In such an environment, there is usually next generation
               | after next generation of these software types, with
               | upgraded (or at least updated) basic technological
               | choices. Some of these choices are recent and possibly
               | bleeding edge, some are old and possibly only good at
               | that time, some prove misguided immediately or in
               | hindsight.
               | 
               | For example I've seen:                 - COM and ASP.Net
               | to Java and JSF (general web apps)       - Forte 4GL
               | (look it up, it was very interesting) to J2EE (general
               | enterprise software)       - MIDP Java to React Native,
               | following mobile phone evolution (mobile app front ends
               | of general enterprise software)       - HTML frame messes
               | to Java applets to JavaScript interactivity (high profile
               | public web site)       - ColdFusion, or something of the
               | sort, to Sharepoint (another high profile public web
               | site)       - SharePoint to a BPEL workflow engine
               | (important workflow-oriented application)
        
               | pjmlp wrote:
               | People keep dismissing COM, unaware that all major
               | Windows APIs since Vista are COM based, even WinRT.
               | 
               | Not using COM for native programming on Windows is being
               | stuck with Windows XP.
        
           | wyager wrote:
           | Business logic features are rarely constrained by the choice
           | of language. All that really changes are probability of
           | implementation error and ease of implementation. I've rarely
           | had the experience that a particular business capability is
           | easy in one language and hard in another language, but vice
           | versa for a different feature.
        
             | VRay wrote:
             | Are you mentally defining "business logic" as the 20 or so
             | lines of code in a program that are completely independent
             | of all input, output, OS libraries, and frameworks?
             | 
             | Pretty much every app, driver, and framework I've worked on
             | across the web, desktop, and mobile has been mostly glue
             | holding different components together. Choosing a different
             | language would require a nearly-complete rewrite in every
             | case except for a tiny section of independent interesting
             | logic (and of course, if you arbitrarily picked some meme
             | language for your "business logic" you'd have to write more
             | glue code to attach it to the rest of your software..)
        
           | marcosdumay wrote:
           | You mean, your manager calls you for an embedded software
           | project, and next thing you know you have to turn your code
           | into an user facing web application?
        
             | pjc50 wrote:
             | Stranger things have happened. At a previous job there was
             | a 20+ year old codebase that targeted embedded Windows CE,
             | which it was requested to be used as the backend of a
             | mobile app. The solution was a bit Heath Robinson, but you
             | could remote desktop onto the server and watch the windows
             | flicker as they processed web requests as if they were
             | input sequences.
             | 
             | Was this mad? Yes. Was it quicker to market than a rewrite?
             | Yes.
        
               | stan_rogers wrote:
               | Translation note: "Heath Robinson" is pronounced "Rube
               | Goldberg" in en/us.
        
             | tialaramex wrote:
             | Also, if things _do_ move this far away from the original
             | vision -- and it can happen especially in a start-up --
             | that 's a rare case where "total rewrite" is a valid
             | software engineering strategy that can deliver benefits
             | out-weighing its cost.
             | 
             | I've done a few total rewrites now, and the _only_ one I am
             | sure was a good idea was for this reason (DPX, the Java re-
             | implementation of Garlik 's core software before it was
             | purchased by Experian Consumer Services many years ago).
        
               | lanstin wrote:
               | I have worked for someone that believes you should
               | rewrite every five years or so, just do the current team
               | knows how it works and also do not needed stuff can fall
               | away. I think it presupposes enough modularity that you
               | can do it without having everything in the org have to
               | change.
        
         | lisper wrote:
         | The thing is, if you start with Common Lisp, it's pretty easy
         | to write a DSL that adds the constraints and provides the
         | guarantees that you need. If you start with Rust or Haskell, it
         | is impossible to remove the constraints those languages impose
         | short of invoking Greenspun's Tenth Law and re-implementing big
         | parts of Common Lisp.
        
           | JadeNB wrote:
           | > The thing is, if you start with Common Lisp, it's pretty
           | easy to write a DSL that adds the constraints and provides
           | the guarantees that you need. If you start with Rust or
           | Haskell, it is impossible to remove the constraints those
           | languages impose short of invoking Greenspun's Tenth Law and
           | re-implementing big parts of Common Lisp.
           | 
           | This surely cuts both ways: if you write a DSL that adds the
           | constraints and provides the guarantees that you need, you've
           | re-implemented big parts of Rust or Haskell.
           | 
           | Or maybe you only need a tiny fraction of the constraints and
           | guarantees that they provide. But it's likely that you'll
           | need more over time, and then you wind up with not just a re-
           | implementation but a _gradually accreted_ , rather than
           | coherently implemented, one, and that's unlikely actually to
           | provide the guarantees it's meant to provide.
        
             | lisper wrote:
             | > you've re-implemented big parts of Rust or Haskell
             | 
             | Maybe. Or maybe all I had to do to turn CL into Haskell is
             | implement the Hindley-Milner algorithm. I didn't have to
             | write a parser or a compiler because I can just re-use
             | those from CL.
        
           | AnimalMuppet wrote:
           | I'm not sure that it would be "pretty easy" to write a DSL
           | that adds the constraints and provides the guarantees that
           | Rust does. I'm not sure that it's that easy to get it
           | consistent and bulletproof.
           | 
           | Yes, you can't remove the constraints of Rust or Haskell
           | (other than using unsafe, I suppose). But if those languages
           | have the constraints that you want, then trying to write that
           | as a DSL instead is... probably hubris. Also wasteful.
        
             | lisper wrote:
             | > I'm not sure that it would be "pretty easy" to write a
             | DSL that adds the constraints and provides the guarantees
             | that Rust does.
             | 
             | It would be a lot easier than inventing Rust, for two
             | reasons:
             | 
             | 1. You don't need to write a parser.
             | 
             | 2. The target language for your DSL compiler can be Common
             | Lisp rather than machine language without sacrificing
             | performance.
             | 
             | Those two things make it absolutely certain that however
             | much work it is to re-invent Rust in CL it will be strictly
             | less work than inventing Rust from scratch.
             | 
             | There are other benefits to this approach as well. For
             | example: if, after implementing your DSL you discover that
             | you've made a sub-optimal design decision somewhere along
             | the line, it will be a lot easier to tweak your DSL than it
             | is to tweak Rust.
        
               | tialaramex wrote:
               | > 1. You don't need to write a parser.
               | 
               | One of the things people _really like_ about Rust is its
               | fantastic error messages. They 're very helpful.
               | 
               | But, having decided you "don't need to write a parser"
               | you're in a tough place for your DSL since there are
               | problems where Lisp's parser will just tell your DSL
               | programmers something went wrong here and leave them
               | clueless as to what exactly is wrong in their program.
               | Rust wouldn't have done that, but of course its compiler
               | owns the _parser_ too so it gets to handle all the corner
               | cases correctly.
        
               | remexre wrote:
               | > The target language for your DSL compiler can be Common
               | Lisp rather than machine language without sacrificing
               | performance.
               | 
               | Uh, how exactly does that work? Which CL impls can e.g.
               | vectorize as well as rustc does?
        
               | AnimalMuppet wrote:
               | "Easier than inventing Rust" is very much not the same as
               | "pretty easy", though...
               | 
               | And in a world where Rust already exists, in most cases
               | it's easier to just use Rust than trying to write "that
               | kind of thing" as a DSL in Lisp.
        
               | lisper wrote:
               | Or maybe using Rust is an instance of the sunk-cost
               | fallacy. It's possible that the net costs of living with
               | Rust's constraints are larger than the cost of re-
               | implementing it in CL. This has undoubtedly been the case
               | for C and C++. The cost of living with C's constraints
               | (or lack thereof) is incalculable, surely billions of
               | dollars, possibly trillions if you add up the costs of
               | all the buffer overflow vulnerabilities that have cropped
               | up through the ages. You could probably write one hell of
               | a CL compiler for a billion dollars.
        
         | tialaramex wrote:
         | > If you need to write a high-performance, heavily-threaded
         | network server that parses malicious binary data, Rust is a
         | great tool because of those restrictions.
         | 
         | If dealing with Untrusted File Formats perhaps you should use a
         | tool purpose-built for Wrangling them Safely, WUFFS.
         | 
         | You won't find a Hello, World example for WUFFS because Hello
         | World prints out text to your console, which is exactly the
         | sort of nefarious stuff bad guys might try to do and so WUFFS
         | doesn't even provide any mechanism you could use to do that
         | even if you wanted to. But it _does_ Wrangle Untrusted File
         | Formats Safely, shrinking your remaining problem space.
         | 
         | For example WUFFS would be appropriate for taking files your
         | users claim are JPEG "photographs" they uploaded for their
         | "profile picture" and turning each one into either a 64x64
         | pixel RGB array or an error without any risk that they seize
         | control of your profile picture program and do goodness knows
         | what else instead.
         | 
         | Although Rust's memory safety means you can achieve confidence
         | a "photograph" doesn't corrupt memory it doesn't require the
         | rigour WUFFS brings to file parsing, so a Rust program could
         | end up confused about unforeseen state while parsing the file.
         | For example in Rust you can write a function that might
         | mistakenly overflow a 32-bit integer and, in production it will
         | just silently wrap. In WUFFS that function won't compile until
         | you either decide explicitly what should happen (e.g. wrapping,
         | saturation) for each overflow, or you trap all the cases where
         | it could overflow as an error. This is very annoying of course,
         | but we're parsing Untrusted File Formats and if we leave
         | anything to chance that will be exploited.
        
           | ekidd wrote:
           | You're completely right, of course.
           | 
           | I'm pretty confident that I can parse untrusted binary data
           | in Rust with nothing worse than a denial of service. (And I
           | have over a billion 'cargo fuzz' iterations to prove it.)
           | 
           | But WUFFS is even more opinionated and strict than Rust, and
           | so it can offer even stronger guarantees. Admittedly, it
           | looks pretty obscure and the documentation is a little light,
           | but it's a great idea.
           | 
           | I am really just done with CVEs, or at least the sort of CVEs
           | that appear in C programs. We know how to prevent so many
           | classes of security holes completely, allowing us to focus on
           | the harder challenges.
        
           | jdblair wrote:
           | I thought this post was one long gag until I found WUFFS on
           | GitHub:
           | 
           | https://github.com/google/wuffs
        
             | scns wrote:
             | VERY good catch, was confused too.
        
         | rightbyte wrote:
         | > Commony Lisp is a deeply opinionated language
         | 
         | How so? You can do whatever paradgim you want in CL. There is
         | no policing involved. Not in the sense of Haskell or Rust at
         | least. CL will let you modify globals or share data willy nilly
         | just fine.
         | 
         | Mixing functions and values in lists is not really a
         | restriction either, it opens up for all kinds of shenanigans.
        
           | zetalyrae wrote:
           | There is a fairly high bedrock of abstraction, mostly because
           | the compiler is available at runtime, the blurring of the
           | distinction between compile-time and run-time, and image-
           | based development. Common Lisp programs can be as fast as
           | Java, but the executables are huge because they have to
           | bundle the entire image inside. And tree-shaking is less
           | effective because the language is so dynamic it's hard to
           | guarantee some code won't ever be called.
           | 
           | And if the abstraction bedrock is high, then problem domains
           | below that bedrock can't be approached with the language.
        
             | lispm wrote:
             | Contrary to popular believe, image support is common, but
             | actually not required by the Common Lisp standard.
             | 
             | Various Common Lisp implementations like ABCL (Common Lisp
             | on the JVM), ECL, CLASP, mocl, ... don't support saving and
             | starting images.
             | 
             | > And tree-shaking is less effective because the language
             | is so dynamic it's hard to guarantee some code won't ever
             | be called.
             | 
             | That depends on the delivery system. For example in
             | LispWorks I can manually remove a lot of functionality:
             | 
             | http://www.lispworks.com/documentation/lw71/DV/html/deliver
             | y...
        
               | zetalyrae wrote:
               | Yes, LispWorks has a powerful tree shaker and I think
               | SBCL now has one as well. Arguably images could get even
               | smaller by distributing the runtime as a shared library.
               | I admit this is an implementation detail.
        
               | lispm wrote:
               | It was already years ago a problem for some companies or
               | organisations wanting to deploy Common Lisp software on
               | tiny (embedded) machines or without GC. IS Robotics
               | (later called iRobot) developed L, a small Common Lisp,
               | for robots with small embedded computers - I think it
               | also has been used on the Roomba devices.
        
               | zetalyrae wrote:
               | Is this a stripped down Common Lisp, or something like
               | Naughty Dog's GOAL[0], basically an embedded assembly
               | DSL?
               | 
               | [0]:
               | https://en.wikipedia.org/wiki/Game_Oriented_Assembly_Lisp
        
               | lispm wrote:
               | It's Common Lisp. See "L - A Common Lisp for Embedded
               | Systems"
               | https://www.researchgate.net/publication/2949173_L_--
               | _A_Comm...
               | 
               | Another one was CLICC : https://github.com/hoelzl/Clicc
               | which has a language definition for CL0 :
               | https://github.com/hoelzl/Clicc/tree/master/doc
               | 
               | There are or were a bunch of such small delivery oriented
               | implementations: Oracle bought one years ago, Gensym has
               | one, mocl is derived from CLICC, ...
        
               | guenthert wrote:
               | "CLiCC is a Common Lisp to C Compiler. [..] CLiCC
               | supports a subset of Common Lisp + CLOS." Isn't it then
               | made obsolete by ECL (a decade or so later)?
        
               | lispm wrote:
               | ECL is based on earlier compilers, which go back to KCL
               | from 1984.
               | 
               | The idea of CLICC was to compile programs to small static
               | C programs. Really small and with little or no dynamic
               | features of Lisp (code loading, runtime compilation,
               | eval, redefinition, ...). It did not have all the bells
               | and whistles of ECL (virtual machine, runtime code
               | loading, debugging, full dynamic Common Lisp, ...).
        
               | ekidd wrote:
               | Hello, former minor L contributor here! (I last worked on
               | it in the late 90s, and I'm only going to talk about
               | stuff that iRobot has mentioned publicly.)
               | 
               | L was actually a subset of Common Lisp. It would run
               | pretty nicely on a machine with 1 MB total RAM and a 16
               | MHz CPU. All the basics were there: lambdas and macros
               | and symbols and modules, and they all worked how they did
               | in Common Lisp. (Or they were close enough that you could
               | write and test in Common Lisp and then cross-compile.)
               | There was a REPL that could run most code, and an ahead-
               | of-time compiler.
               | 
               | It was a fantastically nice environment for embedded
               | systems too small to run something like Alpine Linux. You
               | could compile a program, load it onto actual hardware,
               | and then tweak things interactively using the REPL. And
               | you could invent all kinds of macro DSLs.
               | 
               | Of course, all this becomes moot once your system is
               | large enough to support Alpine Linux and an SSH server.
               | If you can run Linux, then you can use Lisp or Python or
               | PLT Scheme or Rust or anything else you want, and you
               | have half a million open source libraries available.
               | 
               | Still, L is proof that Lisp is a great embedded language
               | for anything with a MB of RAM. You can have a pleasant
               | development environment and tons of flexibility, with an
               | excellent REPL.
        
           | ekidd wrote:
           | The deepest and most radical opinions of Lisp are:
           | 
           | - You should be able to implement Lisp-in-Lisp within a few
           | pages of code. This a profound and remarkable design
           | constraint. (See
           | http://languagelog.ldc.upenn.edu/myl/llog/jmc.pdf (PDF) for
           | an introduction.)
           | 
           | - Syntax would only hide the fact that code is a simple data
           | structure that can be manipulated like any other data
           | structure.
           | 
           | Just like Rust's strong opinions buy you stress-free
           | threading and bit-banging, Lisp's opinions ensure that it's
           | an almost ideal environment for inventing domain-specific
           | language extensions.
           | 
           | But Rust is poorly suited to object graphs where everything
           | mutates everything else, and Common Lisp is poorly suited to
           | proving that a large program obeys certain strict rules.
           | 
           | I love opinionated tools, but I try to choose ones with the
           | "right" opinions for the task at hand.
        
             | pharmakom wrote:
             | The object graph problem really hurts Rust imo. In C# I can
             | just mutate and forget about ownership. In Haskell I can
             | solve this using an immutable graph and a state monad. In
             | Rust it gets pretty awkward.
        
               | chrismorgan wrote:
               | Rust has been my favourite language since 2013. I
               | constantly miss its ownership model when working in other
               | languages (which most commonly means JavaScript for me).
               | 
               | Certainly not everything is a good fit for Rust's
               | ownership model, but honestly most code benefits from it
               | at least a bit, and steadily more and more is being
               | figured out about how to mesh other models into it
               | without too much pain. It's very liberating, protecting
               | from various mistakes that are too easy in other
               | languages (and that's specifically where I miss it).
               | 
               | Ownership modelling can require more effort initially if
               | the problem doesn't match Rust's capabilities very well
               | (or put another way, if the problem doesn't match how
               | _hardware_ works without garbage collection), but
               | subsequently it's liberating to not need to worry about
               | various problems that are ubiquitous in other languages.
               | 
               | I reckon it similar to the debate of static versus
               | dynamic typing, though more subtle. For toys, dynamic
               | typing is adequate and static typing a burden, but as you
               | scale a system up, dynamic typing makes life harder and
               | requires more discipline to avoid ossification or
               | drowning from technical debt. It's taken time, but there
               | has been a significant resurgence in static typing as
               | people slowly come to realise its value through bitter
               | experience in dynamic typing. Like static typing, a
               | strict ownership model requires more effort initially,
               | but makes life easier down the path, and I expect that at
               | least the basic concepts of it will be picked up in more
               | languages new and old over time (this has started
               | already, actually, with Swift and D springing to mind),
               | though it's probably a harder concept to beneficially
               | retrofit to a language than static typing.
               | 
               | You may want to mutate and forget about ownership, but
               | Rust doesn't let you do this, and it's for your own good
               | ;-). In time you get more of a feeling of why it is the
               | way it is and learn to appreciate it.
        
               | db48x wrote:
               | Yea, but in those languages you have a garbage collector
               | working in the background to clean up after you. You
               | don't always want to pay that cost. From time to time I
               | work on an open source application that spends 60-70% of
               | its time in garbage collection, at least on jobs at the
               | extreme end of the size we can handle. I'm starting to
               | think about ridiculous hacks like combining millions of
               | moderately-sized objects into a giant arena. With no
               | pointers into the arena (just indicies), I can eliminate
               | a huge chunk of the time wasted scanning the heap over
               | and over again. But at that point, why am I writing this
               | in Go? My Rust prototype is very incomplete, but for the
               | parts that are implemented it uses way less memory and
               | completes a lot more quickly. And I haven't done _any_
               | performance optimization or ridiculous hacks there yet.
        
               | pitaj wrote:
               | And in Rust you can always drop down to reference counted
               | heap allocated stuff as well.
        
               | db48x wrote:
               | That's true; the prototype uses Rc/Arc for things where I
               | don't yet know for sure how the ownership should really
               | work out, and doubtless a fair amount of that will stay.
        
               | tsimionescu wrote:
               | Reference counting has completely different properties
               | than other forms of automatic garbage collection. In
               | particular, reference counting still requires some amount
               | of ownership tracking, whereas this concept just doesn't
               | exist (for memory resources) with tracing garbage
               | collection.
               | 
               | This is particularly relevant for certain algorithm, such
               | as atomic initialization, that get significantly more
               | complex with in-line ownership tracking.
        
               | db48x wrote:
               | On the other hand, Rc turned out to be just what I needed
               | for a particularly complicated data structure that we
               | use.
               | 
               | http://db48x.net/reposurgeon/rust-port-
               | docs/reposurgeon/path...
               | 
               | Also, because of Rust's wonderful type system it will be
               | fairly straight forward to replace the string keys in
               | this code with interned strings instead. (The amount of
               | strings we use for paths in a large repository is pretty
               | astounding.) That might now be almost possible in Go, if
               | I learn the new generics.
        
             | User23 wrote:
             | > Common Lisp is poorly suited to proving that a large
             | program obeys certain strict rules
             | 
             | As I'm sure you know, a Lisper would probably design a
             | domain specific language that was well suited for such
             | proofs. One of the projects I'd like to get around to one
             | day is a Lisp hosted general purpose DSL[1] based on
             | predicate transformer semantics[2] that combines writing
             | specification and program text into a single interactive
             | experience. Think something along the lines of
             | smartparens[3] strict mode, where the editor doesn't allow
             | you to make an edit that will violate the count("(") ==
             | count(")") invariant and you modify the tree structure with
             | operations like slurp and barf, but a bit more general.
             | 
             | [1] Haha, I know.
             | 
             | [2] https://en.wikipedia.org/wiki/Predicate_transformer_sem
             | antic...
             | 
             | [3] https://ebzzry.com/en/emacs-pairs/
        
               | cwilkes wrote:
               | Write a DSL ... and now you have two problems.
        
       | atombender wrote:
       | Their complaint about Haskell is based on a common misconception.
       | You can do everything in Haskell that you can in a non-pure
       | language, including in-place mutation (e.g. IORef,
       | Data.Vector.Generic.Mutable, etc.), strict functions, manual
       | memory allocation, and so on.
        
         | masklinn wrote:
         | > Their complaint about Haskell is based on a common
         | misconception
         | 
         | Misconception is way too kind a word. And it's quite ironic
         | (though not necessarily unsurprising) seeing the article's
         | namecalling of bad faith as it engages in paragaph after
         | paragraph of bad faith and being plain wrong.
        
           | brabel wrote:
           | I found the article quite interesting and funny (in a good
           | way) and I have trouble understanding why anyone would think
           | it's written in bad faith. He's not attacking you personally
           | for being a Haskeller, he's just making light fun of the fact
           | that Haskell requires monads to do IO and won't let you do it
           | any other way, how is that bad faith?
        
             | herbstein wrote:
             | The fact that monads aren't technically required to perform
             | IO at all but rather is an abstraction that meshes nicely
             | with the Haskell way of doing things. If you don't want to
             | use monads for IO you're certainly going to meet resistance
             | in terms of library support.
        
             | pyrale wrote:
             | > I have trouble understanding why anyone would think it's
             | written in bad faith.
             | 
             | Well, for one, it's hard to start a good-faith discussion
             | about a tool by calling it totalitarian in the introduction
             | paragraph.
        
       | hyperion2010 wrote:
       | This is a wonderful exploration further into the original spirit
       | of that comment and very much reflects the experience that I have
       | had as well.
       | 
       | The only other runtime I have found that has a similar absence of
       | pain is Emacs (though Emacs has its own separate warts unrelated
       | to friction at the repl). I think there is a deeper disconnect
       | between having something that looks like a repl and a real repl
       | which is that for decades Emacs didn't even have a command line
       | style repl, because the repl was everywhere, hidden behind C-x
       | C-e or M-:.
       | 
       | A point of confusion I think is that repl in the lisp world has
       | come to imply much more than that somewhere there is some code
       | that looks like (loop (print (eval (read)))). That level of
       | sophistication of the repl concept was archaic well before common
       | lisp, but the term continued to be used and evolved to signify a
       | runtime that could support interactive development.
       | 
       | For many of the other languages the issue isn't with the form of
       | the interface (almost any language can have a prompt, come up
       | with printed representations that can round trip through their
       | printed representation, etc.) it is that their underlying runtime
       | can't support the kind of redefinition that is needed to avoid
       | the creeping insanity that can arise when code becomes subtly out
       | of sync with data e.g. in Python classes and instances.
       | 
       | Computers are faster now, so the workaround is "restart the
       | runtime" and users hardly notice. Therefore no one bothered to
       | implement what common lisp has. Given how long a cold boot took
       | back in the 70s and 80s, avoiding the restart at all cost was a
       | top engineering priority. Lisps evolved in a significantly
       | different and harsher selective environment than many of the
       | runtimes that came after, and as a result had to solve certain
       | fundamental problems that are practically invisible for users
       | today.
       | 
       | In the time since I wrote the referenced comment on reddit I have
       | also discovered the amazing power of save-lisp-or-die. At the
       | time it hadn't quite clicked for me. I'm guessing that the author
       | has had a similar experience given the title of the series so I'm
       | looking forward to reading more in the future!
       | 
       | In the mean time I also learned a bit of docker, and what I find
       | amusing/sad about it is that slad is basically docker without all
       | the fuss. Sure it can't bring a whole unix system with it, but it
       | is a couple of orders of magnitude smaller and significantly less
       | complex.
        
       | yawaramin wrote:
       | Ironically, the 'bad faith' argument in this post is an example
       | of a bad faith argument.
       | 
       | > These "big idea languages" tend to assert a kind of
       | "programming ideology" over reality, and tend to, in my opinion,
       | distance you from the thing you are trying to do, often, they
       | claim, "for your own good".
       | 
       | Except they don't pull these programming rules out of thin air,
       | they're actually widely acknowledged as the best practices in the
       | domains they're targeting. It's like complaining that SQL
       | database engines don't let you hand-write custom query plans in
       | your queries, 'for your own good'. Well yes-that's exactly the
       | point! They've been finely tuned over decades of research to know
       | how to do it better than humans.
       | 
       | > They are like the neighbor's kids in a totalitarian regime who
       | will report you to the secret police when they think you're not
       | quite as doctrinaire as you ought to be.
       | 
       | Wow! Comparing a technological tool to a fascist regime, because
       | that's _totally_ accurate and appropriate!  /s
       | 
       | > Haskell compiler might be heard to say, "What are you doing?
       | You are a Haskeller and should know better!" ... complains the
       | Rust compiler. "Are you insane? You are a Rustacean, you don't do
       | that kind of thing!"
       | 
       | Yes, of course, compilers are _exactly_ shrill, screaming drill
       | sergeants trying to break you down and indoctrinate you. By
       | contrast, doesn 't Common Lisp seem so mild-mannered and gentle?
       | Of course you'd never want to use anything else!
       | 
       | Now I know it would be a little extreme to call this argument
       | 'gaslighting', but I honestly can't think of a better word.
       | 
       | > Encouraging you to avoid something, however, is quite different
       | from banning it outright.
       | 
       | Yeah, which is why Rust and Haskell actually _don 't_ ban it
       | outright, and in fact why almost every language has 'escape
       | hatches' that allow programmers to do whatever they want, on
       | their own recognizance.
       | 
       | > When you are programming interactively, you are not so much
       | writing code as sculpting and structuring live computer memory.
       | 
       | Great, but once I'm done with that, I need to actually _lock in_
       | the shape of the sculpture so that it can be deployed with
       | confidence.
       | 
       | > True interactive development is also about programming in such
       | a way that your program never crashes. Instead, whenever an
       | unhandled condition is raised, the program starts an interactive
       | debugger.
       | 
       | Great if you're planning to keep a watchful eye on the program
       | constantly. Not very helpful if the program is supposed to run
       | unattended.
       | 
       | > Hell is Other REPLs.
       | 
       | Clever, but even other REPLs allow you to do the crucial bit-i.e.
       | interactively explore an API. And after that, the best languages
       | even allow you to lock in your findings and deploy with
       | confidence!
        
       | PaulHoule wrote:
       | It's still nice to have persistent data structures on your
       | fingertips.
        
       | whartung wrote:
       | I guess I never "got it".
       | 
       | What CL development I've done, it was pretty much "reload it and
       | rerun it" in terms of a development cycle. Mind, these were not
       | large programs. But there was enough global, shared state that
       | needed to be reset that, most of the time, a simple tweak to a
       | function wasn't enough to right whatever wrong was involved. And
       | the reloads weren't arduous anyway.
       | 
       | Sure, for "little work", "local work", tweaking a routine, doing
       | simple tests and such in the listener. It was fine. Very quick
       | turn around.
       | 
       | But when fixing something that was larger in scope? Reload it,
       | rerun it.
       | 
       | I also never "got" the restart and condition systems in CL. Part
       | of this is simply my background, today mostly being in Java where
       | production debugging is doing archaeological digs on deep stack
       | traces.
       | 
       | I get restarts in interactive systems. But my systems were not
       | interactive. They were server based systems, processing zillions
       | of requests. I never saw the utility of a restart on a headless
       | system. I could not imagine a connection stuck in a restart just
       | waiting for me to fix it, debug it, correct it, continue it, or
       | abort it. In contrast to just logging the torrid details of the
       | failure and path to it and using the information in a post
       | mortem.
       | 
       | Do folks get 3am phone calls to open up a server and dig through
       | restarts? That never made any sense to me. On paper, it sounds
       | great, I just never saw any realistic way it would ever apply in
       | any of the work that I did.
       | 
       | Are there times it would have been nice to log in to a server,
       | tweak a piece of code, and let it continue on? Changing the tires
       | of a car on the road? Sure. Occasionally.
       | 
       | Mind, that could just be habitual. Since to me it was a novelty,
       | and one unavailable to me, perhaps I simply don't miss it. Yea,
       | it makes sense when hacking deep space probes. But a generic
       | remote web service type of application in production? To me, not
       | so much.
       | 
       | The idea of hot patching a server is amazing and frightening at
       | the same time. How was the patch tested, do you commit it to VC
       | first, before cut and pasting the DEFUN in to the listener, etc.
       | 
       | The same applies to Smalltalk. The idea of sending out an
       | application that faults and drops the user in to a restart and
       | debugger. What can they do with that? Call me up and talk them
       | through it? I'd much rather them send me the log file with a post
       | mortem I could use. I'm sure ST has ways of capturing stack
       | traces in log files, but, casually, nobody talks about it.
       | 
       | So, I'd love to hear stories about what I'm missing. What
       | applications folks were doing that were leveraging these aspects
       | of the CL experience. How they manifest on system with any
       | transaction volume (you know, a few trx per second). How one uses
       | restarts in a web shopping cart in production.
       | 
       | Make no mistake, in Java, with the applications servers, turn
       | arounds can be pretty long. But, similarly, with code structure,
       | units tests, etc. turn around can be very fast. Make a tweak to
       | the code base, repeatedly run an isolated unit test until it
       | works, then run the larger suite to see what else you broke. That
       | can be quite fast. Not "interactive", but...fast. "Close enough".
        
         | ahefner wrote:
         | Restarts allow composable bridging, across components, of the
         | interactive and batch/"server" worlds, because they can also be
         | invoked programmatically. Of course you wouldn't want the
         | debugger popping in your headless server app, but it could be
         | an incredibly powerful option when you turn it on to debug a
         | specific issue. I don't have any first hand experience in this
         | (never doing any server side work in CL) but Paul Graham had
         | mentioned briefly, somewhere, about ye olde Viaweb and being
         | able to interactively debug and fix customer issues on a live
         | system in real time. It's an appealing proposition, though I
         | think he romanticizes it a bit.
         | 
         | I agree that function-level recompilation of definitions feels
         | alarmingly undisciplined, borderline irresponsible in a
         | production context. On the other hand, in my $work, I can
         | recall sufficient instances where being able to compile in just
         | a little extra targeted debug logging to a running process
         | would've turned completely baffling production issues into
         | something easily explained, sparing a lot of time and hard
         | thinking trying to infer via code review the root cause of
         | issues that no one was able to reproduce internally, and
         | provide much greater confidence of fixes.
         | 
         | It's worth mentioning that on the Lisp machines, the debugger
         | was a first class component of the user interface in a way that
         | might feel very foreign to people accustomed to the Unix
         | command line. It was just a way of inspecting backtraces, it
         | was a essentially a form of interaction where the application
         | could present a problem situation and offer the choices how to
         | proceed.
        
         | invalidname wrote:
         | I have an alert on "production debugging" and this popped up.
         | If you want to get out of that bug hunting in production you
         | might find Lightrun useful (sorry for the thread jack, carry
         | on).
        
         | pixelrevision wrote:
         | _I 'm sure ST has ways of capturing stack traces in log files,
         | but, casually, nobody talks about it._
         | 
         | With smalltalk the user could send you the image in its current
         | state which would provide a lot more context than just a stack
         | trace and a log. I definitely would prefer to pickup a running
         | environment at the point of failure than a log when solving a
         | bug. I cannot imagine how one would keep up with security on a
         | setup like that though.
        
       | platz wrote:
       | This person doesnt understand what Bad Faith actually means. Many
       | people including GP are now using this term as a simple
       | perjorative just to describe things that they don't like or
       | disagree with. Something similar has occured with the term
       | 'gaslighting'.
       | 
       | They also linked to the more questionable entry on wikipedia
       | rather than the more authoritative one. Compare
       | https://en.wikipedia.org/wiki/Bad_faith vs
       | https://en.wikipedia.org/wiki/Bad_faith_(existentialism)
        
       | ilrwbwrkhv wrote:
       | Pure prototypal JavaScript is honestly one of these non
       | authoritarian languages.
       | 
       | At its core it's just:
       | 
       | var obj = {}; obj.a = 10; obj.a; // returns 10
       | 
       | And with browser dev tools you can debug very efficiently.
       | 
       | I would agree that the interactivity of the REPL is not at the
       | same level as CLisp.
        
       | zetalyrae wrote:
       | When I was younger I used to write a lot of Common Lisp. _A lot_.
       | And I also had this idea that Haskell /OCaml/Scheme were strict,
       | ivory tower totalitarian languages, while Common Lisp was this
       | incredibly powerful, expressive, _liberating_ tool. For building
       | organisms, not pyramids, like Sussman said. And the S-expressions
       | were aesthetically appealing to me. But at the time I was working
       | mostly on smaller commercial projects and solo open source
       | projects.
       | 
       | Maybe it's cognitive decline from no longer being a teenager, or
       | the experience of working on larger, O(100,000) line codebases
       | with large teams, but nowadays I find a high degree of dynamism
       | just exhausting. I don't want more expressive power, I want fewer
       | nightmares.
       | 
       | A common problem I faced with Common Lisp is I'd write some
       | module, then go on to write another module that depended upon the
       | previous one, and get an exception through from the first module.
       | Usually a type error. And I'd have to go back, context-switch,
       | fix that, and climb back up to the other level.
       | 
       | With ML/OCaml/Haskell that is far less common. Being able to look
       | at a module and say, "this is done and it's correct", is a very
       | powerful thing. Confidence, rather than safety, is the primary
       | benefit of static typing for me.
       | 
       | And I find that I no longer use the REPL. I've been working on a
       | compiler in OCaml and for some reason Dune won't run utop (lol)
       | so I've just not been REPL'ing and it's not been a problem. The
       | code typechecks. Things work on the first run. If I change
       | something, I get notified what needs updating.
       | 
       | The problem with interactive development is that it's like unit
       | testing: it can prove the presence of bugs but not their absence.
       | Type systems can eliminate large classes of bugs ahead of time.
       | 
       | Just so this is not entirely negative or depressing: there's
       | something beautiful about how maximalist Common Lisp is. It's a
       | big, messy language with every programming paradigm (and you can
       | invent new ones) and different naming conventions in the core
       | language itself. I was learning new things about Common Lisp
       | _years_ into using it productively. And I compared the experience
       | to moving to a huge stately home, that has a room for the piano,
       | a vast attic, a wine cellar, all manner of things, and then
       | trying to move back to a shoebox apartment. Where do I fit the
       | piano? CLOS, the MOP, the Lovecraftian beauty of LOOP and FORMAT:
       | it 's like a wild garden next to the (comparative) spartan
       | minimalism of practically everything else. And it's beautiful.
        
         | tomcam wrote:
         | Helpful real-world perspective, thanks. What is Dune in this
         | context?
        
           | cyberbanjo wrote:
           | A composable build system for OCaml, uses s-exps for config
           | lang
           | 
           | https://github.com/ocaml/dune
        
         | yawaramin wrote:
         | > for some reason Dune won't run utop (lol)
         | 
         | It could be that you don't have a 'library component' defined
         | in the project. Its dune file will look like e.g.:
         | (library           (name lib))
         | 
         | When you have that, 'dune utop lib' will open the REPL and load
         | the library.
        
         | [deleted]
        
       | chrismorgan wrote:
       | > _These "big idea languages" tend to assert a kind of
       | "programming ideology" over reality, and tend to, in my opinion,
       | distance you from the thing you are trying to do, often, they
       | claim, "for your own good"._
       | 
       | Another angle on this for a language like Rust is that Rust is
       | designed the way it is because it's favouring reality over
       | programming ideology, distancing you from the thing you're trying
       | to do when it's something that doesn't map to the reality of how
       | computers work very well--and yes, that reality does end up
       | leading to ideology, but Rust is the way it is because of reality
       | more than because of ideology.
       | 
       | But then Lisp! Why, Lisp is the _epitome_ of programming ideology
       | over reality! Ask me to name a "big idea language" under the
       | provided definition and the first language (well, family of
       | languages) that springs to my mind is Lisp. It's not just
       | unopinionated, it's _aggressively_ unopinionated, which is an
       | opinion, an ideology (to say nothing of the doctrine of
       | S-expressions), and one that flies in the face of how computers
       | actually work; so that you pay at least a performance price to
       | use such languages, and often other prices too, like with
       | threading, as ekidd's comment mentions.
       | 
       | There's a reason Lisp machines died.
        
       | Rich_Morin wrote:
       | The article closes with a discussion of "true interactive
       | development". I was particularly interested in this and wondered
       | how IEx (Elixir's REPL) compares to Common Lisp's. So, I started
       | this thread on the Elixir Forum: https://elixirforum.com/t/hell-
       | is-other-repls-how-does-iex-c...
        
         | denvaar wrote:
         | A few things I like about IEx:
         | 
         | - Supports tab completion, shortcut keys like ctrl-a/ctrl-e and
         | probably more that I don't ever use.
         | 
         | - Top notch support for unicode and supports color (IO.inspect
         | with [color: true])
         | 
         | - EZ access to docs (Just type h in front of what you want to
         | know about)
         | 
         | - You can recompile your project without leaving the repl
         | (keeping whatever state you have)
         | 
         | - You can access the history of what has been returned using
         | the v function
         | 
         | - Remote shell support (type `h IEx`) to learn more.
         | 
         | ---
         | 
         | One thing I don't like: If you get mixed up and forget how many
         | braces or parenthesis you need to close, you get stuck. I
         | usually have to quit iex to get out. There may be a better
         | solution to this that I'm just not aware of
        
           | sfusato wrote:
           | _There may be a better solution to this that I 'm just not
           | aware of_
           | 
           | When that happens, I simply enter _' iex> end'_ which would
           | return a SyntaxError, and start over but without having to
           | restart iex.
        
       | continuational wrote:
       | > The language seems designed to encourage you to be you and to
       | shape the language to suit your individual style, problem
       | domains, or interests.
       | 
       | This is cool for the solo programmer.
       | 
       | Teams and organizations need some amount of standardization if
       | they want their programmers to be able to maintain each others
       | code. At that point, none of the coolness of Lisp remains, and
       | you're better off using a less dynamic language that imposes more
       | of a standard style.
        
         | masklinn wrote:
         | > This is cool for the solo programmer.
         | 
         | Who doesn't come back to their program after 6 months of doing
         | something else.
        
           | masklinn wrote:
           | Not sure why i'm getting downvoted. When I come back to a
           | project 6 months after I last touched I might as well have
           | never heard of it for all I remember.
        
         | lispm wrote:
         | It's not necessarily so. It works also for groups. Example: at
         | a time when 'object orientation' was still kind of new (early
         | 80s) lots of Lisp-using groups were developing OO-extensions
         | for Lisp: Flavors, LOOPs, CommonObjects, ObjectLisp, ... These
         | language extensions were used in groups at TI, Xerox, HP,
         | Symbolics and others. For example Symbolics wrote much of their
         | operating system and their applications using Flavors (->
         | several groups of people used it as a standard language
         | extension -> incl. their customers).
         | 
         | With the Common Lisp standardization, a subgroup was given the
         | task to decide on a standard object-oriented extension or to
         | develop their own. After several years of work, a core group
         | with a large group of supporters and implementors then defined
         | a standard language extension to integrate object-object-
         | oriented programming in Common Lisp - the Common Lisp Object
         | System, which is now widely used in Lisp.
        
       | peterkelly wrote:
       | > _" True interactive development is also about programming in
       | such a way that your program never crashes. Instead, whenever an
       | unhandled condition is raised, the program starts an interactive
       | debugger."_
       | 
       | That's great until someone else tries to use your program.
       | 
       | I'll take the compile-time safety checks, thanks.
        
         | dunefox wrote:
         | As we know no bugs are present in statically typed programs.
        
           | IshKebab wrote:
           | Nobody claimed that, but there definitely _are_ fewer. 15%
           | fewer according to the only study I 've seen that objectively
           | measured it.
        
             | dunefox wrote:
             | Link? I'm not certain that there's a way to objectively
             | measure that on a scale that matters.
        
       | taeric wrote:
       | I have been musing with the thought that so many IDE environments
       | are mainly about integrating other tools for you to make your
       | program. Common Lisp does seem to be about integrating your
       | program into your environment in a way that isn't that common
       | anymore. (I know there are other "image" based languages. They
       | seem to have fallen by the roadside, though. Common Lisp, mostly,
       | included.)
        
       | pjc50 wrote:
       | This is remarkably similar to the advocacy made by lots of C/C++
       | programmers that the language is more useful because it lets you
       | make mistakes, which is all well and good until someone buffer
       | overflows your server and leaks the personal data of a million
       | people / your cryptocurrency keys / whatever.
       | 
       | The opinion of Rust and Haskell is precisely that making mistakes
       | is _bad_ , regardless of your "provocative" opinion that it might
       | be good, and that the language should be a
       | https://en.wikipedia.org/wiki/Poka-yoke against certain
       | categories of mistakes that have been found to cost the industry
       | billions of dollars in failure.
       | 
       | (What do the Clojure-for-web-services people do? Presumably that
       | doesn't drop web requests to an interactive debugger, or does it?
       | Or is that irrelevant because this is only concerned with Common
       | Lisp?)
       | 
       | This kind of advocacy seems to be endemic in LISP and FORTH: a
       | tool produces great results when used by one or two idiosyncratic
       | "genius" developers working in near-isolation on problems of
       | their choice. It tends not to work nearly so well beyond that.
        
         | disconcision wrote:
         | > What do the Clojure-for-web-services people do
         | 
         | Not precisely what you're asking but I have, very occasionally,
         | REPLed into remote clojure servers and rewritten running code
         | live to fix time-critical bugs in production. But I'll admit
         | i'm unsure whether this is an argument for or against Xtremely
         | interactive development.
         | 
         | Darklang does offer something they call trace-based development
         | where unhandled requests basically do initiate an interactive
         | process to write handling code. I'm under the impression though
         | that this is not intended for production time.
         | 
         | In general I'm loath to make any generalization about what kind
         | of mistakes are good or bad and when. Luckily we have a
         | language landscape which lets people make up their own minds.
        
           | Matthias1 wrote:
           | Darklang's traces are not the same thing as a REPL. The user
           | gets sent a normal 400 error, and the request trace is saved
           | so that later, the developer can "replay" the request (on the
           | live server, with full context).
           | 
           | While Dark does support the general idea of doing live code
           | in production, it's not a Lisp dialect and is very much a
           | functional language.
        
         | fiddlerwoaroof wrote:
         | This whole discussion reminds me of
         | http://tomasp.net/blog/2015/failures/ . What I've noticed is
         | that programmers are largely divided into camps based on the
         | cost they're willing to pay to avoid runtime errors:
         | Haskell/Rust developers tend towards one side while
         | Erlang/CL/Clojure tend to the other.
         | 
         | My experience is that if you make the edit->run->edit loop
         | tight enough, you can recover a lot of the guarantees available
         | through static checks via a couple of alternate techniques and,
         | for some of us, the resulting workflow is a lot more pleasant
         | way to spend the day than tweaking the code up front until the
         | compiler accepts it.
        
         | didibus wrote:
         | > What do the Clojure-for-web-services people do? Presumably
         | that doesn't drop web requests to an interactive debugger, or
         | does it? Or is that irrelevant because this is only concerned
         | with Common Lisp
         | 
         | In CL the interactive debugger is only one option of how to
         | handle uncaught exceptions. You can set it to just crash or log
         | or whatever you want for production use case.
         | 
         | In Clojure it defaults to the host runtime default handling, so
         | like in Java it'll throw all the way to the thread and kill it.
         | Unless you explicitly handle it somewhere.
         | 
         | In JavaScript browser, it'll just get logged in the browser
         | console.
         | 
         | And I'm not sure what NodeJS does, maybe it crashes the
         | process?
         | 
         | But I'd say Clojure is more like the OCaml of Lisp, it nudges
         | you strongly towards pure functional programming, managed
         | memory and thread safe behavior. But it isn't strict about it,
         | which allows you to still get a fully interactive development
         | workflow.
         | 
         | It's a bit like how in Python private methods are a convention,
         | turns out defaults and conventions tend to be followed pretty
         | well by programmers. But having it not be strict means it can
         | accommodate certain weird use cases when needed.
         | 
         | I like to think of it like where Haskell and Scala have safety
         | guarantees, Clojure instead has a safety protocol. In practice
         | both seem to yield on average similarly correct programs.
        
       | Ericson2314 wrote:
       | These posts complaining about programming languages shackles also
       | seem to concieve of programming as a highly individualistic
       | endeavor. I don't that that's a coincidence.
       | 
       | I think type systems and other restrictions are really good
       | precisely because they help me better trust and collaborate with
       | others.
        
       | wyager wrote:
       | > There is something vaguely totalitarian about these big idea
       | languages. The Rust or Haskell compilers, for example, insist on
       | policing whatever private understanding you might have about the
       | meaning of your code. They are like the neighbor's kids in a
       | totalitarian regime
       | 
       | This statement doesn't even slightly ring true for me. These
       | compilers are more like infallible personal assistants who keep
       | track of all the crap that I don't want to. The Haskell compiler
       | in particular has never suggested I stop doing something that
       | didn't turn out to be a bad idea under more careful
       | consideration.
       | 
       | The rust compiler is more restrictive (mostly due to lack of
       | ability to express some types, like quantified or higher kinded
       | types), but still is fundamentally working on my behalf.
        
       | [deleted]
        
       | skybrian wrote:
       | I'm interested in how people using different languages manage
       | this style of "developing in the debugger" and then (eventually)
       | checking in finished code.
       | 
       | Two systems I'm familiar with:
       | 
       | * In Flutter, you write code in the normal way, in an editor or
       | IDE. If you want to change code while the program is running, you
       | edit code in the normal way and hit a key to reload it. This
       | works when stopped at a breakpoint. But the changes you can make
       | using hot reloading seem somewhat limited; you couldn't do a
       | major refactoring.
       | 
       | * In an Observable notebook, you edit a cell and then any other
       | cells that depend on it automatically run, like a spreadsheet.
       | You don't normally use a debugger, though the browser's debugger
       | does work. In some cases when you're using advanced features, you
       | might need to reload the page, but usually you don't.
       | 
       | How do people work using Common Lisp? In other languages? In
       | particular, do you write code in one place (using the repl) and
       | copy-paste it to another place?
        
         | tgbugs wrote:
         | The workflow for slime/sly and swank/slynk described at the end
         | of the article is standard if you are in the Emacs world.
         | 
         | Sometimes I work in an Org file, sometimes in a lisp file
         | directly. In both cases I will C-x C-e (eval-last-sexp) or run
         | blocks, or the whole file (depending on Org vs lisp file). It
         | is copy and paste on steroids (same for slime-eval-defun were
         | you can be working deep inside some large function and update
         | the whole without changing your view).
         | 
         | There are some examples of developing in the debugger in [0]
         | around 55 minutes (linked). This video also has great examples
         | of other crazy interactive development workflows and is worth
         | watching in its entirety if you are interested in this kind of
         | thing.
         | 
         | 0. https://youtu.be/bl8jQ2wRh6k?t=3299
        
           | skybrian wrote:
           | Hmm. The article doesn't really describe what it's like but I
           | skimmed the SLIME manual. So you basically point at the code
           | you want to run, and this isn't necessarily how you would run
           | it in production. It looks like SLIME mode requires you to
           | install an interpreter hook in the startup code for the
           | process that you want to debug, so you can run commands that
           | way?
           | 
           | This seems like it would make the most sense for event-driven
           | code. Evaluating a script is another kind of event. Many
           | systems have a way to execute commands.
           | 
           | I'd guess this style of development encourages people to
           | structure their systems as essentially command interpreters,
           | even if the commands normally come from a UI.
           | 
           | The interesting bit will be deciding how the data structures
           | that persist between commands are implemented. For example in
           | server-side web apps, the shared state is often a database,
           | so state is external to the process being debugged. The
           | architecture limits what you can do directly, but you can try
           | out SQL queries.
        
       | zmmmmm wrote:
       | The problem with REPL-driven development is that when taken too
       | far it makes you capable of creating programs that simply cannot
       | be understood by someone without the "lived experience" of the
       | path taken to arrive the solution in the REPL.
       | 
       | The very property that makes it liberating - that you can
       | instantly try things out and see mistakes - allows you to elevate
       | up a level or two in complexity of what you can do. But the
       | future readers of your code don't have this advantage.
       | 
       | While you _can_ apply discipline and use it to work to find the
       | simplest, clearest solution, there is nothing that forces that
       | and you can just as easily arrive at something that looks and
       | works like magic to someone coming to it fresh.
        
         | CraigJPerry wrote:
         | I don't know. I can write incomprehensible code in any
         | language. It is to some extent the default. I have to work hard
         | so as not to be the poor future reader (myself) in 6 months
         | thinking "where do i even begin...?"
         | 
         | I find the repl encourages me to iterate quickly and when i set
         | out to solve a problem, one of my goals is to express the
         | solution in a nice way.
         | 
         | The repl just reduces the cost of me trying different ways to
         | achieve that. Whether i leave behind horrible or nice code
         | isn't really a factor of the repl, it's a factor of whether i
         | started with an intention to write understandable code. I
         | usually do and the repl's instant feedback and state retention
         | makes that vastly easier.
         | 
         | Sometimes i don't set out with that intention though, i
         | frequently have to process lots of text and regexp is just the
         | easiest way for me usually - even though i accept it's utterly
         | impenetrable to many once you go beyond 10-20 chars or so in an
         | expression. No repl involved in producing fairly impenetrable
         | code.
        
         | codetrotter wrote:
         | I've been reading the book ANSI Common Lisp by pg, and decided
         | to work through some of the exercises that piqued my interest.
         | 
         | Similar to what you are talking about here, I worked on one of
         | the problems for a while. Not in a REPL, but in a similar
         | manner. And I arrived at a solution that is not advanced or
         | anything, but which I was quite satisfied with. It occurred to
         | me then that my solution to the problem I was working on really
         | only made sense because I had worked through the problem.
         | 
         | For this reason I left a printout of two of my hash tables in
         | the code. Because when you see what data they actually have in
         | them it becomes quite obvious what the code is doing. Whereas
         | without those two printouts you'd have to "simulate" in your
         | brain what the code is doing.
         | 
         | And so for that reason I left the printouts in my code.
         | 
         | But this also ties into a more general problem in software
         | development which is that even though our tools are becoming
         | really really good and our computers really really fast there
         | is still a long ways to go in terms of how deeply and how
         | completely we can really see what's going on.
         | 
         | I was thinking about this recently when I was at the dentist to
         | pull a tooth. It was not a fun experience but even so I saw
         | that the dentist had both the skill and the tools for making
         | the operation. And in particular the tools he had at his
         | disposal allowed him to understand and to operate on my teeth.
         | And that got me thinking once again about the lack of
         | visibility that we have when we develop software.
        
         | jlg23 wrote:
         | > The problem with REPL-driven development is that when taken
         | too far it makes you capable of creating programs that simply
         | cannot be understood by someone without the "lived experience"
         | of the path taken to arrive the solution in the REPL.
         | 
         | Can you provide an example for this? Intuitively I'd say you
         | are right, but, OTOH, even after 20 years of CL-hacking (which
         | probably does more REPL-driven development than any other
         | language), I cannot come up with an example...
        
         | dunefox wrote:
         | > But the future readers of your code don't have this
         | advantage.
         | 
         | Future readers also don't have access to my brain state when
         | I'm implementing the program. Whether in Lisp or in Haskell or
         | in C or in Python. This is kind of a nonsensical point.
         | Iterating over a solution in Java and in Lisp it amounts to the
         | same thing: code in a file. There's nothing keeping you from
         | documenting.
        
         | rini17 wrote:
         | This is only a problem when the image diverges from the source.
         | I heard of cases where there was precious functionality only
         | compiled in the lisp image without any source code because it
         | was programmed and debugged directly into REPL. But this is a
         | mistake of programmer misusing a powerful tool, not a good
         | practice.
         | 
         | Emacs makes doing this too easy, though. I prefer slimv which
         | has no extra repl buffer. It forces you to edit the sources and
         | invoke REPL with snippets from there - ideally, whole file
         | stays compilable all the time. Or at least to use a scratch
         | file, to later get back to and refactor. Any programmer bitten
         | by their own unreadable code should learn to do that and resist
         | siren calls of repl patching. But then, unreadable code is
         | possible in any language.
        
           | mst wrote:
           | I rather like the Interlisp-D approach where while you edit
           | in a live environment it then syncs the modified versions
           | back out to lisp source files on disk as its standard/natural
           | workflow.
           | 
           | I suspect the way you're working with slimv is at least
           | somewhat comparable in terms of making getting that part
           | right the path of least resistance, which is itself a form of
           | developer experience ergonomics.
        
         | lispm wrote:
         | See 'Bottom-up programming':
         | http://www.paulgraham.com/progbot.html
        
       ___________________________________________________________________
       (page generated 2021-08-29 23:00 UTC)