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