[HN Gopher] Maybe We Can Have Nice Things
       ___________________________________________________________________
        
       Maybe We Can Have Nice Things
        
       Author : ChrisSD
       Score  : 64 points
       Date   : 2021-02-17 17:12 UTC (2 days ago)
        
 (HTM) web link (noncombatant.org)
 (TXT) w3m dump (noncombatant.org)
        
       | pessimizer wrote:
       | > However, for all of NPM's problems, at least it is a package
       | management system at all! It's easy to pick on NPM (or
       | predecessors like CPAN, or CTAN, or...), but even at its worst
       | it's a huge improvement over manually managing dependencies (such
       | as by manually vendoring them into your source tree, or just
       | telling the user to install such-and-such libraries before
       | attempting to compile).
       | 
       | Isn't the labor-intensive nature of manually wrangling all of
       | these resources a constraint that pushes people to minimize
       | version conflicts and micro-dependencies? The reason that you see
       | these problems in NPM (and other version control systems) is
       | because the automation _removes_ a constraint.
        
         | cwp wrote:
         | Yeah, good point. The reason NPM can have packages like left-
         | pad is that it works so well. It's easy to publish a package,
         | it's easy to find one, it's easy to install and use a package.
         | So sure, why NOT make a package for one function that you use
         | all over the place?
         | 
         | There's a downside to all this, but ultimately it's to NPM's
         | credit: the tools are so easy to use that people... use them.
        
           | brundolf wrote:
           | I feel the need to mention, whenever leftpad comes up, that
           | it's impossible for that incident to ever happen again.
           | Packages can no longer be unpublished after a brief window
           | (48 hours I think?), so if you lock to specific patch numbers
           | your dependencies are effectively immutable
        
       | NetOpWibby wrote:
       | I wonder if something like supply-chain exists for npm, looks
       | super useful.
        
       | mtalantikite wrote:
       | Cargo from Rust and go fmt from golang have been some of my
       | favorite things these past few years. Cargo isn't perfect, as the
       | author points out, but wow is it great to have it as a major part
       | of the language's ecosystem.
       | 
       | I recently got access to GPT-3 and was playing around with it,
       | which meant breaking out Python for the first time in a while,
       | and I just don't understand how anyone deals with it. Poetry
       | seems much better than what I remember Python ever having, but
       | it's not official of course and not everyone uses it. I somehow
       | was hitting dependency issues with packages stuck in Python 2 and
       | I couldn't believe that's _still_ happening -- it's been over a
       | decade now. Even ran into some very minor Black vs PEP 8 things.
       | Somehow every time I touch Python I find myself fighting the
       | tooling, which may just be my ignorance or bad luck.
       | 
       | Very thankful for Cargo and go fmt so I can just get on with
       | things!
       | 
       | (And also the author is right, the Rust community is super
       | welcoming. Thanks for that as well!)
        
         | cwp wrote:
         | It's not your imagination. Python tooling is awful.
         | 
         | If I may be forgiven a bit of preaching, I'll point out that
         | the fundamental problem with the Python ecosystem is that
         | package definitions are executable python code. This means they
         | have their _own_ dependencies, and are subject to all the
         | pitfalls of general purpose programs - the halting problem,
         | non-determinism etc. The Python community keeps trying to fix
         | this by writing a better installer, and it 's never going to
         | work.
         | 
         | NPM and Cargo, with their declarative package definitions, are
         | built on a far more stable base!
        
           | rectang wrote:
           | Whatever Perl's other issues, through the introduction of
           | `META.json` CPAN successfully augmented a system designed for
           | executable install scripts like `Makefile.PL` with a
           | declarative package metadata mechanism. Why can't Python do
           | the same?
        
           | rst wrote:
           | Not sure I agree with this diagnosis -- Ruby gemspecs (for
           | components) and Gemfiles (for dependency lists) are both
           | executable Ruby code, but packaging troubles at the
           | Pythonesque level are rare.
           | 
           | The trouble I've had with Python packaging had a lot less to
           | do with the specs being executable code than with there being
           | tools with overlapping use cases which interpret them in
           | different ways, none of which seems to cover all use cases,
           | and whose documentation of what they expect in package specs
           | is sometimes in conflict. If the specs turned to JSON-with-
           | comments tomorrow, those problems wouldn't go away.
        
           | neolog wrote:
           | http://python-poetry.org/ solves the problem by providing
           | declarative package definitions.
        
           | dralley wrote:
           | Python packaging tooling and infrastructure is exactly like
           | this: https://xkcd.com/2347/
           | 
           | Everybody uses it, but the work of maintaining it is
           | thankless and doesn't receive nearly as much attention as you
           | would expect relative to the popularity of the language.
           | 
           | The unfortunate thing is that there's only a very small
           | number of people actively maintaining, and they have so
           | little time available that it's nearly impossible even to
           | contribute something substantial.
           | 
           | I bore witness to an attempt by my colleagues to replace the
           | legacy XML-RPC APIs (which have been publicly declared as
           | deprecated for years with no replacement even today) which
           | completely fell through because the maintainers didn't want
           | to (or have time to) provide any feedback whatsoever. It was
           | basically requested that a 100% implementation with 100% test
           | coverage be provided before they would even spend the time to
           | _evaluate the API design concept_ and give feedback.
        
       | AtlasBarfed wrote:
       | "A key innovation of C++ was to introduce RAII, which essentially
       | 'piggybacks' on the value of the stack and enriches it with a lot
       | more power."
       | 
       | I haven't written C++ in two decades, but it was my impression
       | that heap allocated resources are not on the stack.
       | 
       | The major problem with C++ isn't resources can be used used after
       | freed, it's that they are never freed if you don't clean up when
       | your RAII is popped off the stack via destructor or other "event
       | handler methods".
       | 
       | Isn't RAII more of a pattern implemented atop the core
       | constructor/destructor? It's kind of a stretch to call it a
       | fundamental feature of C++ like GC is to java/jvm.
       | 
       | Has valgrind "solved" memory leaks as a primary concern/danger in
       | C++?
        
         | bluGill wrote:
         | You should look into modern c++ - at least C++11. While it is
         | possible to write the code with all the problems you state,
         | C++11 adds some neat features that mean in practice you rarely
         | do. Sure I can put something on the heap and forget to free it
         | - but in practice I don't put anything on the heap without
         | first wrapping it in something on the stack so that when the
         | thing on the stack goes away my thing on the heap does too.
         | This can get really complex fast to deal with edge cases, but
         | most of the time the complexity is hidden and so doing the
         | right thing that always works is just as easy as putting
         | something on the heap that I have to remember to free latter.
         | 
         | Most people use address sanitizer not valgrind for the few
         | times they have a memory leak. In general though the problem is
         | solved not by tools but by using the modern C++ features.
         | Valgrind is really only useful when you need to deal with C
         | interfaces. (note interfaces, the code on either side need not
         | be written C)
        
         | RcouF1uZ4gsC wrote:
         | RAII is a core fundamental feature of C++ and I would argue
         | that constructors and destructors are the implementation.
         | 
         | With RAII (using vector, string, unique_ptr, etc), memory leaks
         | are pretty much a solved problem in modern c++. The biggest
         | memory danger in C++, in my opinion, is dangling references
         | where you have a reference to an object (either on the stack or
         | heap) that has been destructed.
        
         | hawski wrote:
         | What's a fundamental feature for C++ then, if not RAII?
         | 
         | GC may not be the fundamental feature of Java the language, but
         | it is certainly a fundamental feature of JVM.
        
         | ChrisSD wrote:
         | The idea in RAII is you create a type that's solely responsible
         | for acquiring the resource and freeing it (and little else).
         | This idea is indeed fundamental to modern C++ design. For
         | example, smart pointers.
         | 
         | When created, the pointer lives on the stack. The resource it
         | points to lives elsewhere but that's no concern to RAII. When
         | the stack variable goes out of scope the resource will
         | automatically be freed (via the destructor)
         | 
         | One place where this can break down in C++ is in copy and
         | moves. The acquire/release works simply enough using
         | constructors/destructors but correctly handling copies and
         | moves has turned out to be harder to get right. And when you
         | add in other objects that may refer to the resource object...
         | well things can quickly become even harder to handle unless
         | you're very careful.
        
       | romaniv wrote:
       | _> Programming languages advance by introducing new constraints._
       | 
       | Among other things.
       | 
       |  _> throw is a little bit too goto-y for my taste_
       | 
       | Conceptually, throw is not at all like GOTO. It's not a control
       | structure, but a communication tool. Throw interrupts execution
       | of logic in a subsystem that encounters abnormal conditions and
       | _communicates those conditions to the containing system_. The
       | second part is the most important one.
       | 
       |  _> However, for all of NPM's problems, at least it is a package
       | management system at all! It's easy to pick on NPM [...], but
       | even at its worst it's a huge improvement over manually managing
       | dependencies_
       | 
       | This really depends. In many ways NPM made _introduction_ of new
       | dependencies easier, while making handling dependencies over long
       | periods of time a much more hairy problem. I don 't like that
       | most devs today assume that versioned packages is the only way
       | you can do things. Many also don't understand that "dependencies"
       | is not a real thing, it's an aggregate label that is applied to a
       | set of problems that _happen_ to be solved by package managers.
       | Problems like discovery, deprecation and compatibility checking.
        
         | carapace wrote:
         | > throw is not at all like GOTO
         | 
         | They are the same thing.
         | 
         | To see it clearly, replace each exception with a label, each
         | "try" by a no-op, each "catch" by a label statement, and each
         | "throw" by a GOTO statement.                   try:
         | foo()         catch BarException             baz()
         | def foo():             ...             throw BarException
         | ...
         | 
         | Is like:                   CALL foo         GOTO end
         | LABEL bar         CALL baz         LABEL end
         | LABEL foo         ...         GOTO bar         ...         RET
         | 
         | Either you have relatively simple exceptions and the equivalent
         | GOTOs would also be simple enough to be not "considered
         | harmful"; -or- you have complicated exceptions, which is really
         | an ad hoc state machine, and you might be better off making
         | that explicit rather than hiding it in a tangle of
         | try/catch/throw control-flow logic, eh?
        
       | losvedir wrote:
       | I had taken for granted that RAII was a good and useful pattern,
       | from my dabbling in rust and what I've overheard in C++. So I was
       | surprised and interested that Zig rejects that, in favor of a
       | `defer` construct somewhat a la Go to free resources. I'm not
       | sure I agree, but it shows that "advances" in PL design aren't
       | universally agreed as such.
       | 
       | I guess what I'm getting at is I agree with the article that it's
       | great that programming languages develop new ideas and
       | constructs, but it's not always obvious which ones are "winners".
       | How do you even really judge that? The quantity and quality of
       | software in the language? Polls of developers?
        
         | bluGill wrote:
         | There isn't a good answer. RAII is a great thing that I use all
         | the time. However it requires deterministic destruction which
         | in turn means some form of reference counting garbage
         | collection. Garbage collection research has found non-reference
         | counting garbage collection has a lot of advantages. So as a
         | language designer you need to choose, better garbage collection
         | at the expensive of non-deterministic destruction, or worse
         | garbage collection but you get RAII. There isn't a clear
         | winner.
         | 
         | For about 80% of all garbage this doesn't matter because it
         | turns out the reference count is always one until it goes to
         | zero so you don't need to track it, and in turn this means the
         | better garbage collection algorithms I mentioned above are not
         | better in this case. However this rule of thumb comes from C++
         | where training (based on real performance needs of the type of
         | code you might write in C++) keeps people from doing the types
         | of operations that would increment the reference count. Which
         | is to say even if Java could figure which cases can use this
         | rule (this appears like variant of the halting problem to me),
         | they would probably discover that the number there is enough
         | less as to not make it worth adding.
         | 
         | Worse, in C++ there is a tiny number of data structures with
         | circular references that you cannot use reference counting to
         | deal with. Thus as a C++ programmer will sometimes have to
         | write some other garbage collector for the edge cases, and
         | these don't get RAII deterministic destruction.
         | 
         | Note that I didn't state the advantages of better garbage
         | collectors. I am not an expert here, so I'm leaving it as an
         | exercise to the reader. Just know that there are advantages to
         | them that make them a great choice for some problems despite
         | losing RAII! If you are designing a language you should
         | probably look into this early. (I was going to say a hybrid is
         | the worst of both worlds - but maybe I'm wrong so I'll let you
         | figure that out)
        
         | tsss wrote:
         | There are still people who disagree that the world is round and
         | a very very large portion of the populace is religious when
         | it's abundantly clear that religion is bullshit. Go is an
         | outlier with a huge media machine behind it and it will
         | disappear back into the abyss as soon as Google stops pushing
         | it with no contributions to programming language research at
         | large.
        
       | DonnyV wrote:
       | I love when I read about languages and they completely ignore
       | .NET and C#. Like it hasn't advanced in leaps and bounds.
        
         | pjmlp wrote:
         | And everything that has existed outside Bell Labs and UNIX.
        
         | tptacek wrote:
         | The programs Palmer is talking about already exist, and are
         | already implemented in C and C++, languages without managed
         | runtimes. Rust is interesting here because it is memory safe
         | and also doesn't have a managed runtime.
        
         | carapace wrote:
         | A programmer's opinions of various technologies is highly
         | trajectory-dependent. The problem of uneven distribution of
         | lore is exacerbated by two startling factors: we tend to be
         | ignorant of the history of our field; and we are fashion-driven
         | to a fault. I suspect that's how you get educated intelligent
         | people with, um, unusual ideas about things. (E.g. I've worked
         | with fellows who did not know who Alan Kay was, or what Prolog
         | is.)
         | 
         | In re: Rust, Graydon Hoare gave a talk on the history of
         | compilers in which he doesn't mention Prolog.
         | https://thenewstack.io/rust-creator-graydon-hoare-recounts-t...
         | I suppose he just didn't think it was relevant, but it's
         | possible even he is a little ignorant?
         | 
         | I see a ray of sunshine in the form of a nascent and inchoate
         | new paradigm that could be called "Categorical Programming",
         | Conal Elliott has the scent: http://conal.net/papers/compiling-
         | to-categories/ CT provides a mathematical formalism for finding
         | the most highly factored forms of various functions/programs,
         | enabling a kind of "convergence" of code to its most efficient
         | form. But that's a big ol' tangent.
         | 
         | My point is, it's often more informative to try to understand
         | the context in which something someone says makes sense, than
         | to just decry them as wrong. Oh, and to remind folks to read
         | your history!
        
         | ibraheemdev wrote:
         | .NET will always be looked at as enterprisey, and old school. I
         | agree that it has advanced a lot, but I don't think anything
         | will change that perspective.
        
           | MereInterest wrote:
           | And Windows-only. I know that Mono has made some good
           | strides, but relying on it for the language itself adds a
           | much bigger risk factor.
        
             | DonnyV wrote:
             | This is exactly the mindset of a lot of developers that
             | don't even bother looking at .NET. If you did. You would
             | know that .NET is far beyond offering Mono. .NET and Mono
             | merged back in 2016 to form .NET Core. Which was supported
             | on all major OS platforms and was open sourced. .NET 5 was
             | released in 2020 which merges the rest of .NET, including
             | WinForms. Now all open sourced. .NET 6 has already been
             | announced with a cross platform UI framework. So
             | enterprisey and old, it is not.
        
               | ibraheemdev wrote:
               | Yup .NET Core being cross-platform and open-source is a
               | huge deal, and Blazor looks really cool, but I still
               | don't think the way people look at C# is going to change.
        
       | PaulHoule wrote:
       | "throw" is a nice thing in my mind and not at all like goto.
       | 
       | Before there was "throw" people wrote C programs with no error
       | handling at all, or globals used to pass error conditions, or
       | something like "Optional" with no syntactic sugar and 250% more
       | coding errors.
       | 
       | Error handling is ultimately a non-local concern (e.g. this
       | subroutine failed because somebody cut a fiber in Nebraska.)
        
         | timw4mail wrote:
         | Error handling is an everywhere concern. In general,
         | recoverable errors are more local, and unrecoverable errors
         | should crash/throw.
        
           | PaulHoule wrote:
           | "Recoverable" means different things.
           | 
           | Where I work we would rather the app crash than display
           | incorrect data -- accuracy is critical to us.
           | 
           | The engine control unit in your car has multiple degraded
           | modes it operates in when parts like the oxygen sensor fail
           | -- in that business the "show must go on" but so does the
           | "check engine" light.
           | 
           | Fly-by-wire airliners like the A320 and B777 also have a
           | number of degraded modes that pilots are aware of and there
           | is a huge database of experience that proves the concept.
           | 
           | The 737 MAX hack tried to bolt a primitive form of flight
           | envelope protection onto the old hydraulic control system but
           | didn't consider that the plane would sometimes encounter
           | degraded conditions such as one of two AoA vanes failed.
        
             | timw4mail wrote:
             | Absolutely, which is one of the reasons good error handling
             | is difficult. The context of use for the current code
             | determines how errors can be handled.
        
           | XorNot wrote:
           | This doesn't feel like it translates into anything actionable
           | though.
           | 
           | For example: I've never had a problem with Python's approach
           | of defaulting to exception handling because it seems to
           | bridge that gap nicely: either you can handle it in your
           | local catch block, or you want to send it higher up to see if
           | something with hopefully more context knows what to do.
           | 
           | Most error handling where "error's are values" is still
           | ultimately doing this exact thing somehow it's just a
           | question of with what implementation.
        
         | [deleted]
        
         | jstimpfle wrote:
         | Here are a bunch of error-handling strategies that I'm aware of
         | (which one is valid depends on the situation).
         | 
         | - Die immediately / assume the error won't happen, but still
         | code defensively.
         | 
         | - Throw an exception (in languages that support this). setjmp()
         | / longjmp() also falls in this category, although it is hard to
         | do correctly.
         | 
         | - Return an error code to the calling function
         | 
         | - Contain the error in a data structure.
         | 
         | The last is a truly object-oriented approach. In larger systems
         | this is the one that scales. Its advantage is that it decouples
         | the error detection from the handling (temporal decoupling) and
         | allows the error to be handled asynchronously. This is a
         | property that exceptions don't have.
         | 
         | Exceptions are, in my experience, not nice to use. If you
         | _actually_ want to handle errors (i.e. not use them only as a
         | glorified die() function) the code gets really ugly - you tend
         | to use them like error return values, but with more verbose
         | syntax (having two return mechanism is bad since it reduces
         | compatibility of code).
         | 
         | I know a situation where exceptions can be useful: when
         | interpreting a chunk of code in a sandbox in a synchronous
         | fashion - i.e. you call the interpreter function of that
         | virtual process, and that process either completes successfully
         | or terminates with an exception. The virtual process is then
         | cleaned up. There is no possibility to interrupt the
         | interpretation and resume some time later.
         | 
         | This is more of a toy setting, though. You don't write complex
         | systems like that.
        
       ___________________________________________________________________
       (page generated 2021-02-19 23:02 UTC)