1.  

    I wonder what it’s like to become very used to something like this and then suddenly not have it.

    1.  

      I have been thinking about this too. This is currently the biggest downside of optimising and modifying your system like this.

    1. 53

      This is why we can’t have good software. This program could literally have been an empty file, a nothing at all, a name capturing the essence perfectly.

      I’m not sure I could disagree more strongly. An empty file only has the true behavior because of a bunch of incredibly non-obvious specific Unix behaviors. It would be equally reasonable for execution of this file to fail (like false) since there’s no hashbang or distinguishable executable format to decide how to handle it. At a somewhat higher level of non-obviousness, it’s really weird that true need be a command at all (and indeed, in almost all shells, it’s nottrue is a builtin nearly everywhere).

      true being implementable in Unix as an empty file isn’t elegant—it’s coincidental and implicit.

      1. 15

        I mean, it’s POSIX specified behavior that any file that is executed that isn’t a loadable binary is passed to /bin/sh (”#!” as the first two bytes results in “implementation-defined” behavior), and it’s POSIX specified behavior that absent anything else, a shell script exits true.

        It’s no more coincidental and implicit than “read(100)” advances the file pointer 100 bytes, or any other piece of standard behavior. Sure, it’s Unix(-like)-specific, but, well, it’s on a Unix(-like) operating system. :)

        1. 24

          It’s precisely specified, yes, but it’s totally coincidental that the specification says what it does. A perfectly-reasonable and nearly-equivalent specification in an alternate universe where Thomson and Ritchie sneezed five seconds earlier while deciding how executables should be handled would have precisely the opposite behavior.

          On the other hand, if read(100) did anything other than read 100 bytes, that would be extremely surprising and would not have come about from an errant sneeze.

          1. 34

            Black Mirror Episode: The year is 2100 and the world is ravaged by global warming. The extra energy aggregated over decades because non executables went through /bin/sh caused the environment to enter the tipping point where the feedback loops turned on. A time machine is invented, where one brave soul goes back in time with a feather, finds Thomson and makes him sneeze, saving humanity from the brink of extinction. But then finds himself going back to 2100 with the world still ravaged. Learns that it was fruitless because of npm and left-pad.

            1. 12

              it’s totally coincidental that the specification says what it does.

              This is true of literally all software specifications, in my experience.

              1. 8

                Surely we can agree that it is far more coincidental that an empty executable returns success immediately than that e.g. read(100) reads 100 bytes?

                1. 7

                  Why isn’t 100 an octal (or a hex or binary) constant? Why is it bytes instead of machine words? Why is read bound to a file descriptor instead of having a record size from an ioctl, and then reading in 100 records?

                  Just some examples. :)

                  1. 5

                    Obviously, minor variations are possible. However, in no reasonable (or even moderately unreasonable) world, would read(100) write 100 bytes.

                    1. 12

                      Pass a mmap’ed pointer to read, and it shall write. :)

              2. 10

                The current (POSIX) specification is the product of historical evolution caused in part by /bin/true itself. You see, in V7 Unix, the kernel did not execute an empty file (or shell scripts); it executed only real binaries. It was up to the shell to run shell scripts, including empty ones. Through a series of generalizations (starting in 4BSD with the introduction of csh), this led to the creation of #! and kernel support for it, and then POSIX requiring that the empty file trick be broadly supported.

                This historical evolution could have gone another way, but the current status is not the way it is because people rolled out of bed one day and made a decision; it is because a series of choices turned out to be useful enough to be widely supported, eventually in POSIX, and some choices to the contrary wound up being discarded.

                (There was a time when kernel support for #! was a dividing line between BSD and System V Unix. The lack of it in the latter meant that, for example, you could not make a shell script be someone’s login shell; it had to be a real executable.)

                1. 9

                  The opposite isn’t reasonable though. That would mean every shell script would have to explicitly exit 0 or it will fail.

                  Every. Shell. Script.

                  And aside from annoying everyone, that wouldn’t even change anything. It would just make the implementation of true be exit 0, instead of the implementation of false be exit 1.

                  And read(100) does do something besides read 100 bytes. It reads up to 100 bytes, and isn’t guaranteed to read the full 100 bytes. You must check the return value and use only the amount of bytes read.

                  1. 7

                    It’s not obvious to me that an empty file should count as a valid shell script. It makes code generation marginally easier, I suppose. But I also find something intuitive to the idea that a program should be one or more statements/expressions (or functions if you need main), not zero or more.

                    1.  

                      So if you run an empty file with sh, you would prefer it exits failure. And when you run an empty file with python, ruby, perl, et al., also failures?

                      Why should a program have one or more statements / expressions? A function need not have one or more statements / expressions. Isn’t top level code in a script just a de facto main function?

                      It’s intuitive to me that a script, as a sequence of statements to run sequentially, could have zero length. A program with an entry point needs to have at least a main function, which can be empty. But a script is a program where the entry point is the top of the file. It “has a main function” if the file exists.

                      1.  

                        I think whatever the answer is, it makes equal sense for Perl, Python, Ruby, shell, any language that doesn’t require main().

                        In my opinion, your last argument begs the question. If an empty program is considered valid, then existing is equivalent to having an empty main. If not, then it isn’t.

                        In any case, I don’t mean to claim that it’s obvious or I’m certain that an empty program should be an error, just that it seems like a live option.

                      2.  

                        Exactly. It sounds like arbitrary hackery common in UNIX development. Just imagine writing a semi-formal spec that defines a program as “zero characters” which you pass onto peer review. They’d say it was an empty file, not a program.

                        1.  

                          I guess true shouldn’t be considered a program. It is definitely tied to the shell it runs in, as you wouldn’t call execv("true", {"/bin/true", NULL}) to exit a program correctly. for example. true has no use outside of the shell, so it makes sense to have it use the shell’s features. That is why now it tends to be a builtin. But having it a builtin is not specified by POSIX. Executing file on the other end, is, and the spec says the default exit code it 0 or “true”. By executing an empty file, you’re then asking the shell to do nothing, and then return true. So I guess it is perfectly fine for true to jist be an empty file. Now I do agree that such a simple behavior has (loke often with unix) way too many ways to be executed, ans people are gonna fight about it for quite some time! What about these?

                          alias true=(exit)
                          alias true='/bin/sh /dev/null'
                          alias true='sh -c "exit $(expr `false;echo $? - $?`)"'
                          

                          The one true true !

                          1.  

                            It depends upon the system. There is IEFBR14, a program IBM produced to help make files in JCL which is similar to /bin/true. So there could be uses for such a program.

                            It also has the distinction of being a program that was one instruction long and still have a bug in it.

                            1.  

                              “That is why now it tends to be a builtin.”

                              Makes sense. If tied to the shell and unusual, I’d probably put something like this into the interpreter of the shell as an extra condition or for error handling. Part of parsing would identify an empty program. Then, either drop or log it. This is how such things are almost always handled.

                        2. 1

                          That would mean every shell script would have to explicitly exit 0 or it will fail.

                          I don’t see how that follows.

                          Once the file is actually passed to the shell, it is free to interpret it as it wishes. No reasonable shell language would force users to specify successful exit. But what the shell does is not in question here; it’s what the OS does with an empty or unroutable executable, for which I am contending there is not an obvious behavior. (In fact, I think the behavior of running it unconditionally with the shell is counterintuitive.)

                          And read(100) does do something besides read 100 bytes.

                          You’re being pedantic. Obviously, under some circumstances it will set error codes, as well. It very clearly reads some amount of data, subject to the limitations and exceptions of the system; zero knowledge of Unix is required to intuit that behavior.

                          1. 7

                            I don’t see how that follows.

                            You claim the exact opposite behavior would have been equally reasonable. That is, the opposite of an empty shell script exiting true. The precise opposite would be an empty shell script—i.e. a script without an explicit exit—exiting false. This would affect all shell scripts.

                            Unless you meant the opposite of executing a file not loadable as an executable binary by passing it to /bin/sh, in which case I really would like to know what the “precise opposite” of passing a file to /bin/sh would be.

                            You’re being pedantic. Obviously, under some circumstances it will set error codes, as well. It very clearly reads some amount of data, subject to the limitations and exceptions of the system; zero knowledge of Unix is required to intuit that behavior.

                            No. Many people assume read will fill the buffer size they provide unless they are reading the trailing bytes of the file. However, read is allowed to return any number of bytes within the buffer size at any time.

                            It also has multiple result codes that are not errors. Many people assume when read returns -1 that means error. Did you omit that detail for brevity, or was it not obvious to you?

                            1. 6

                              If a file is marked executable, I think it’s quite intuitive that the system attempt to execute. If it’s not a native executable, the next obvious alternative would be to interpret it, using the default system interpreter.

                          2.  

                            Saying the behavior is totally (or even partially) coincidental is a bit strong. You’re ignoring the fundamental design constraints around shell language and giving the original designers more credit than they deserve.

                            Consider this experiment: you pick 100 random people (who have no previous experience to computer languages) and ask them to design a shell language for POSIX. How would all of these languages compare?

                            If the design constraints I’m talking about didn’t exist, then it would indeed be random and one would expect only ~50% of the experimental shell languages to have a zero exit status for an empty program.

                            I strongly doubt that is what you would see. I think you would see the vast majority of those languages specifying that an empty program have zero exit status. In that case, it can’t be random and there must something intentional or fundamental driving that decision.

                            1. 6

                              I don’t care about how the shell handles an empty file. (Returning successful in that case is basically reasonable, but not in my opinion altogether obvious.) I’m stating that the operating system handling empty executables by passing them to the shell is essentially arbitrary.

                              1.  

                                The reason for the existence of human intelligence isn’t obvious either but that doesn’t make it random. A hostile environment naturally provides a strong incentive for an organism to evolve intelligence.

                                As far as the operating system executing non-binaries with “/bin/sh” being arbitrary, fair enough. Though I would argue that once the concepts of the shebang line and an interpreter exist, it’s not far off to imagine the concept of a “default interpreter.” Do you think the concept of a default is arbitrary?

                            2.  

                              It’s precisely specified, yes, but it’s totally coincidental that the specification says what it does.

                              laughs That’s really taking an axe to the sum basis of knowledge, isn’t it?

                          3.  

                            yes an empty file signifying true violates the principle of least astonishment.However if there were a way to have metadata comments about the file describing what it does, how it works, and what version it is without having any of that in the file we’d have the best of both worlds.

                            1.  

                              true being implementable in Unix as an empty file isn’t elegant—it’s coincidental and implicit.

                              But isn’t this in some sense exactly living up to the “unix philosophy”?

                              1.  

                                No.

                              2. 0

                                To me, the issue is whether it is prone to error. If it is not, it is culture building because it is part of the lore.

                              1. 3

                                I like being aware of this. Sometimes people try to improve things that are perfectly ok already and when you examine why, it’s usually because they feel that they need to do something. It happens outside of software and engineering too,

                                1.  

                                  Isn’t it better though to have an ELF binary which just returns 0, rather than having to start a shell interpreter every time you want to invoke /bin/true? Also, when every other part of a collection of tools (in this case GNU coreutils) follows a convention (i.e that --version prints version information and --help prints usage information), is it really better that /bin/true is the one binary which doesn’t follow that convention?

                                  This seems like a classic case of making the world a little bit better.

                                  1.  

                                    Isn’t it better though to have an ELF binary which just returns 0, rather than having to start a shell interpreter every time you want to invoke /bin/true?

                                    I can see an alternate viewpoint where it just seems like bloat. It’s yet another chunk of source to carry around to a distribution, yet another binary to build when bootstrapping a system, and other holdovers.

                                    Also the GNU coreutils implementation of true is embarrassing. https://github.com/coreutils/coreutils/blob/master/src/true.c is 65 lines of C code and accepts 2 command-line arguments, which means that the binary has to be locale aware in those situations.

                                1. 7

                                  My sense of the error handling issue is that Go implicitly says “We’re not going to let you hide or abstract away your error handling. We’re going to force you to make it explicit so that you really think about it.”

                                  When you start to use the chaining style presented in the article as an alternative (in other languages), you can hide a lot with bad names - lead the reader of the code to believe the handling isn’t there. Throwing exceptions puts people in that mode too.

                                  1. 3

                                    Great stuff. Re the conditionals, I look at it this way: An object is a choice.

                                    When you have sub-type polymorphism, you can make a choice in one part of a program and create an object than embodies it. When the object is bound, the choice is made and the conditional can disappear from the contexts where the object is used.

                                    1. 1

                                      I agree and use that model, and it makes me think “I answered this question back when I constructed this object”. That’s something I can certainly see as a source of confusion.

                                    1. 4

                                      This is a problem with any language or library. You need to know what is available in the Python library and what it does to use it effectively. You need to know the binaries in /bin to use the shell effectively. And so on.

                                      It’s just like learning a human language: Until you use the vocabulary enough to get comfortable, you are going to feel lost, and spend a lot of time getting friendly with a dictionary.

                                      1. 8

                                        This is a problem with any language or library. You need to know what is available in the Python library and what it does to use it effectively. You need to know the binaries in /bin to use the shell effectively. And so on.

                                        I think this probably misses the point. The Python solution was able to compose a couple of very general, elementary problem solving mechanisms (iteration, comparison), which python has a very limited vocabulary of (there’s maybe a half dozen control constructs, total?), to quickly arrive at a solution (albeit while a limited, non-parallel solution, one that’s intuitive and perhaps 8 times out of 10 does the job). The standard library might offer an implementation already, but you could get a working solution without crawling through the docs (and you could probably guess the name anyways).

                                        J required, owing to its overwhelming emphasis on efficient whole-array transformation, selection from a much much much larger and far more specialized set of often esoteric constructs/transformations, all of which have unguessable symbolic representations. The documentation offers little to aid this search, complicating a task that was already quite a bit less intuitive than Python’s naive iteration approach.

                                        1. 5

                                          For a long time now, I’ve felt that the APL languages were going to have a renaissance. Our problems aren’t getting any simpler, so raising the level of abstraction is the way forward.

                                          The emphasis is on whole array transformation seems like a hindrance but imagine for a second that RAM becomes so cheap that you simply load all of your enterprise’s data in memory on a single machine. How many terrabytes is that? Whole array looks very practical then.

                                          For what it’s worth, there is a scheme to the symbols in J. You can read meaning into their shapes. Look at grade-up and grade-down. They are like little pictures.

                                          J annoys me with its fork and hook forms. That goes past the realm of readability for me. Q is better, It uses words.

                                          What I’d like to see is the entire operator set of, say, J brought into mainstream languages as a library. Rich operations raising the level of abstraction are likely more important than syntax.

                                          1. 5

                                            J annoys me with its fork and hook forms. That goes past the realm of readability for me.

                                            In textual form I agree. The IDE has a nice graphical visualization of the dataflow that I find useful in ‘reading’ that kind of composition in J code though. I’ve been tempted to experiment with writing J in a full-on visual dataflow style (a la Max/MSP) instead of only visualizing it that way.

                                            1. 2

                                              I find it a lot easier to write J code by hand before copying it to a computer. It’s easier to map out the data flow when you can lay it out in 2D.

                                              1. 1

                                                Have you looked at Q yet?

                                              2. 1

                                                That would be a very useful comparison of the usability of a compact text syntax vs visual language. I imagine that discoverability is better with a visual language as by definition it is interactive.

                                              3. 2

                                                I started implementing the operators in Scala once - the syntax is flexible enough that you can actually get pretty close, and a lot of them are the kind of high level operations that either already exist in Scala are pretty easy to implement. But having them be just a library makes the problem described in the article much much worse - you virtually have to memorize all the operators to get anything done, which is bad enough when they’re part of the language, but much worse when they’re a library that not all code uses and that you don’t use often enough to really get them into your head.

                                                1. 1

                                                  It could just be a beginning stage problem.

                                                  1. 1

                                                    It could, but the scala community has actually drawn back from incremental steps in that direction, I think rightly - e.g. scalaz 7 removed many symbolic operators in favour of functions with textual names. Maybe there’s some magic threshold that would make the symbols ok once we passed it, but I can only spend so much time exploring the possibility without seeing an improvement.

                                                    1. 1

                                                      Oh. I’d definitely go with word names. Q over J. To me, the operations are the important bit.

                                            2. 5

                                              The difference is that Python already is organized by the standard library, and has cookbooks, and doesn’t involve holistically thinking of the entire transformation at once. So it intrinsically has the problem to a lesser degree than APLs do and also has taken steps to fix it, too.

                                              1. 2

                                                How easy is it to refactor APL or J code? The reason I ask is that I have the same problem with the ramdajs library in JavaScript, which my team uses as a foundational library. It has around 150 functions, I don’t remember what they all do and I certainly don’t remember what they’re all called, so I often write things in a combination of imperative JS and ramda then look for parts to refactor. I’m interested to hear whether that’s possible with APL, or whether you have to know APL before you can write APL.

                                            1. 11

                                              Dijkstra may have seen this too. Look at the tail end of this quote.

                                              “APL is a mistake, carried through to perfection. It is the language of the future for the programming techniques of the past: it creates a new generation of coding bums.” - Edsger W.Dijkstra

                                                1. 10

                                                  Dijkstra is the battle rapper of computer scientists

                                                  1. 2

                                                    Imagining him battling Alan Perlis.

                                              1. 9

                                                It’s fun to watch generational counter-balances like this occur. Yes, in the past there was an ethic of: “Real programmers program in their off-time to get better.” Now, we try to counter it, which is good, but it is better to encourage people to take responsibility.

                                                Figure out what your career goals are. Figure out what you need to know. If they don’t match, take some time to learn. Some employers will help you with this. Some won’t. Sometimes you want to learn something because you’re just curious. Sometimes you just want to move to a different employer. When you take responsibility, what you need to do is obvious. The worst outcome is to be upset when other people take the positions you want because you denied the possible effect of not making particular career investments.

                                                1. 1

                                                  Technical debt is something that always creates an internal debate for me. In some cases, I believe that it is our responsibility as engineers to “fight” against it, through the use of well-known refactoring techniques and being efficient on time management to improve code even when adding new features. But sometimes (or most of the time) you always have the pressure to add new features to the system, and how you sell that the feature is going slow due to not being able to “just add the feature” but instead you have to move things around to accommodate the new feature?

                                                    1. 6

                                                      I have learnt never to say “Technical debt”.

                                                      If you say “Technical debt” to the business end, you’ve lost already.

                                                      Because then you have to explain what the problem is exactly. And you run in to the Manager’s Syllogism.

                                                      You know the one, “Manager’s are Important People. They know and Understand Important Things, if XXX was Important, They would Know All About it.”

                                                      Ok, I’m exaggerating for humorous effect, but the point is compared to everything else on the list of things they want done, Technical Debt is really low on the list…. and most times falls of the list of things To Do This Cadence. Again.

                                                      Or worse, “So you’re saying this mess exists because your Managers are Bad and your colleagues are Worse? And now you expect me to trust you to go off on a long rewrite?”

                                                      I distrust rewrites.

                                                      They are never as Good as Hoped and always take way way longer than expected to get up to full functional equivalency. In fact, usually never do. In fact, I’d argue most of the benefits of a rewrite is in deleting cruft nobody really needs.

                                                      So start there.

                                                      Delete cruft as you go along every time you see it.

                                                      If someone yells and really wants it back… hey that’s what version control is for.

                                                      So merely say, “It will take X amount of time.” Why so Long? “Because that is the way the existing code is. (You can start explaining it to them until their eyes glaze over..)”

                                                      And then fix the existing code.

                                                      When you need to fix a bug, write a unit test that stimulates it. Then tidy up the code until it is so simple the bug is obvious. Fix the bug.

                                                      When you need to add a feature, brace exiting required functionality in unit tests, write a unit test to add the new feature, watch it fail, clean up the code until it’s trivial to add the new feature, add the new feature, watch it pass.

                                                      Technical Debt? What’s that? Heard of it once, not a useful concept in practice.

                                                      1. 2

                                                        Thank you so much for this. Really really useful!

                                                        1. 1

                                                          Thank you! Will read it :)

                                                      1. 3

                                                        “OOP was concerned with representing reality”

                                                        This is a widely held belief but it’s neither the most useful definition nor original intent. Likewise classification through hierarchical taxonomies is not an essential aspect of OOP, but an unfortunate offshoot of the popular “representing reality” mindset.

                                                        They original intent and most useful concept of OOP is essentially message passing. Smalltalk’s early (ca 1972) evolution and Hewitt’s actors influenced each other through first person exchanges between Kay and Hewitt.

                                                        1. 3

                                                          They original intent and most useful concept of OOP is essentially message passing. Smalltalk’s early (ca 1972) evolution and Hewitt’s actors influenced each other through first person exchanges between Kay and Hewitt.

                                                          I think there’s a couple of common misconceptions here. The first is that Smalltalk was the “original intent” of OOP. The first OOP language was Simula-67 (ca 1968), which had polymorphic dispatch and inheritance. IIRC Smalltalk and Simula were developed independently and each contributed ideas to “modern” OOP.

                                                          The second is that there is an “essential aspect” of OOP at all. There is no “essential” OOP in the same way there is no “essential” FP: Lisp, APL, and ML are all considered FP languages despite being wildly different from each other. I’d argue that there’s a few common “pedigrees” of OOP that are all influential and all contribute ideas that most modern OOP languages consider “essential”:

                                                          • Simula: Modularization, Dynamic Dispatch
                                                          • Smalltalk: Message Passing
                                                          • CLU: Abstract Data Types, Generics
                                                          • Eiffel: Contracts, Class Invariants*

                                                          I think part of the reason we assume Smalltalk is the “foundation” of OOP is because the other foundational languages, for the most part, aren’t well-known today.

                                                          *I’ve read that CLU had contracts first, but I can’t find a primary source on this.

                                                          1. 4

                                                            Alan Kay, The Early History of Smalltalk…

                                                            “The whole point of OOP is not to have to worry about what is inside an object… data and control structures be done away with in favor of a more biological scheme of protected universal cells interacting only through messages that could mimic any desired behavior.”

                                                            “Though it has noble ancestors indeed, Smalltalk’s contribution is a new design paradigm—which I called object-oriented—for attacking large problems of the professional programmer, and making small ones possible for the novice user.”

                                                            “[Dedicated] To SKETCHPAD, JOSS, LISP, and SIMULA, the 4 great programming conceptions of the sixties.”

                                                            “It is not too much of an exaggeration to say that most of my ideas from then on took their roots from Simula—but not as an attempt to improve it. It was the promise of an entirely new way to structure computations that took my fancy.”

                                                            “In 1966, SIMULA was the “better old thing,” which if looked at as “almost a new thing” became the precursor of Smalltalk in 1972.”

                                                            1. 2

                                                              Is Haskell missing the whole point of Functional Programming because it’s not a Lisplike?

                                                              1. 2

                                                                The way I see it, running all the combinations of features in OO/Actor and then running combinations for all of the features in FP the elemental thing seems to be ‘tell’ in OO/Actor and ‘ask’ in FP. ‘Tell’ enables asynchrony and ‘ask’ enables ‘laziness.’ They are quite high but different mechanisms for modularity.

                                                                1. 2

                                                                  I’m sorry. Who is claiming Haskell is “missing the whole point of functional programming”?

                                                                  1. 1

                                                                    It’s a point of comparison. While AK was very important in The foundations of OOP, he doesn’t have a ground to claim he’s the founder or even necessarily the most significant contributor. And just because modern OOP diverges from his vision does not mean that it’s in some way diminished because of that.

                                                                    Similar to how Haskell is very different from lisp but still counts as an FP language.

                                                                    1. 1

                                                                      If the history you’ve got is not the history you want, just make it up as you go along. I have no interest in a discuss like this.

                                                                      1. 1

                                                                        What I’m saying is that equating OOP with just Alan Kay is historically incorrect. He played a big role, yes. But so did Kristen Nygaard, David Parnas, and Barbara Liskov. Their contributions matter just as much and ignoring them is historical revisionism.

                                                                        1. 1

                                                                          I never equated OOP with “just Alan Kay”. I equated him correctly with coining the term and inventing the first (completely) OOP language Smalltalk. Parnas and Liskov played roles, certainly in modularity, information hiding, and abstract data types. Later on in the history of OOP I recall around 1986-7 Luca Cardelli published a paper which was intended to provide a taxonomy of OOP-related terms. He define languages like CLU (abstract data types, parametric polymorphism, but no message-passing / runtime dispatch) as “object-based” where he reserved “object-oriented” for the latter.

                                                                          Certainly Kay never gave Nygaard the short shrift, emphasizing in countless forums Simula’s huge role in OOP.

                                                                2. 2

                                                                  That’s indeed what Alan Kay thinks, but he’s not exactly a neutral historian here, since he’s obviously a pretty strong partisan on behalf of the Smalltalk portion of that lineage.

                                                                  1. 2

                                                                    “The Early History Of Smalltalk” is a reviewed, published (ACM HOPL) history of the early years of OOP by the person who originally used the term. If you wish to refer to that as being a “strong partisan” then I guess that’s your privilege.

                                                                    1. 2

                                                                      If we’re going to use the ACM as an authority here they officially recognize Dahl and Nygaard as the inventors of OOP.

                                                                      1. 1

                                                                        The ACM is correct and this coincides exactly with Kay’s attribution of Simula being a “better old thing” and “almost a new thing”. While Simula introduced all the key concepts of OOP, Kay coined the term to imply the new thing, which is “everything is an object” not Simula, the almost new thing, which is ALGOL + objects for some things.

                                                                        It’s a fine line distinguishing the two, which Kay more than acknowledges and which does not disagree with the ACM’s recognition of Nygaard and Simula in the history of OOP.

                                                            1. 6

                                                              It’s not just software. ‘Just’ is the most dangerous word in life.

                                                              1. 2

                                                                Just say no to it.

                                                              1. 4

                                                                Good article. The way I like to describe the modularity benefit is that laziness allows you to make something general and impose the edges later. That general thing that you make is often easier to understand because it doesn’t have special cases.

                                                                A simple example: When I was working on a vi clone in Haskell I made a screen buffer data structure that was simply my line buffer (a list of lines) with an infinite list of tildes appended to it. From that point on, rendering was easy no matter what the size of the window was in relation to the size of the original line buffer. You just did a take of the number of lines you needed for the window. If I hadn’t done that, the rendering code would’ve mixed buffer size, window size, and tilde padding concerns.

                                                                1. 4

                                                                  Yegge knows how to pitch, but when I saw what he was pitching it reminded me of one the problems I have with many startups today: automating things that might be better left un-automated.

                                                                  For instance, I really enjoy going out to restaurants and browsing brick and mortar stores. For someone who works from home these are nice opportunities for random social interaction. I don’t think the world will ever devolve to a complete “shut-in culture”.. we can make the argument that these innovations will free us up for more high quality social interaction, but it still feels troubling to me. Even the idea of stores without floor employees is troubling.

                                                                  That’s all I want to say except that I do agree that the article is somewhat off-topic here. I hesitated before replying because the reason I like lobsters is that we tend to stay on track with tech rather than digressing into social topics. This is the last I’ll say on this.

                                                                  1. 6

                                                                    The real problem is that ‘hosted’ comes along for the ride with most business models.

                                                                    1. 2

                                                                      This. I would gladly pay a one time fee to license software. If my organization can stand up a server for our product we should be able to put up another one for logging/analytics/whatever else gets shoehorned into being a third party service. It shouldn’t be rocket science to sell a piece of software with an install script, or heroku procfile, or whatever config looks like for AWS.

                                                                      1. 1

                                                                        Hmm, I dunno. I mean, sure having a way to buy the software for those who self-host is great. But I would not always prefer it for a lot of non-core stuff.

                                                                        I get this argument for something like New Relic, which is really actually expensive (like $100 per machine). But operations work is pretty annoying, and having to not manage it is pretty great.

                                                                        We used to be running a self-hosted Sentry instance. And it was basically fine! Worked as intended. But because it got real use, we needed to maintain it. So we moved over to their hosting and pay the $30/month to not deal with this. And get updates and all the goodness.

                                                                    1. 3

                                                                      Such back-and-forth movement in IT circles reminds me of an old Dilbert strip: https://thestandard.org.nz/wp-content/uploads/2011/06/dilbert-reorganisation.jpg

                                                                      1. 2

                                                                        It’s so true. It’s often hard to find people who appreciate that the “X is bad, let’s all move to Y” mentality requires a fair look at the good parts of X and the bad parts of Y to be an informed move. The grass is always greener.

                                                                        It’s better still to look a why these shifts occur. For instance, the decentralized web people acknowledge that the web started that way, but, as far as I’ve seen, haven’t looked into why centralization occurs.

                                                                        1. 1

                                                                          I think we know why centralisation occurs, and not just on the web.

                                                                          Marketing.

                                                                          1. 5

                                                                            Like hell. I used to centralize stuff because it was easier. Within one box, you have the ability to ignore the failure modes of a distributed system. Many jobs can also be done single-threaded to ignore the failings of concurrent operation. In one company and within certain distance, your clusters can use low-latency links make a limited version of a distributed system easier to handle. When distributed and/or truly P2P, you just turned a bunch of jobs that are straight-forward using 80’s-90’s method into things that are difficult in ways that might keep surprising us well past 2018.

                                                                            1. 2

                                                                              Centralization prioritizes rare catastrophic failure over small consistent failure. So it’s easier until it’s all the sudden not.

                                                                              1. 1

                                                                                Yes, that’s the tradeoff of centralization. Taleb beats this into the ground in his writing.

                                                                                I look at centralization/decentralization as a design parameter. The choice is contextual. The thing most people don’t seem to realize yet is that systems tend to centralize, so if you opt for decentralization you have to be aware of that and work against it somehow.

                                                                                Bringing it back to software, the natural tendency for us in software is to keep adding to existing abstractions (classes, services, etc) unless we apply some diligence and either break things down periodically or set up our practices so that we are making new things rather than hacking onto existing ones.

                                                                                1. 1

                                                                                  That can and does happen with some but is a lifecycle detail. Centralized services are easier to verify in the single, non-distributed case with well-understood models of failure when using high-availability clusters and recovery strategies. Note that centralized architectures with fault-tolerance are also usually modular with failure isolation and swappable components. That’s why there’s plenty of OpenVMS and NonStop systems out there with years of uptime with some having over a decade. There are battle-tested mechanisms for achieving that where it’s not hard for developers versus systems not designed that way.

                                                                                  Microservices’ track record on both verifying their interactions and overall reliability hasn’t been as good that I can see. They also tend to use components that weren’t individually designed for high reliability with ad-hoc, less-proven protocols for ensuring availability across those components. The correctness conditions are also more spread out. Although not necessary, the dependencies for correctness also seem to change at a much-faster pace which increases odds of systematic failure. Add to decentralized/distributed nature, you might also be looking at a lot of heisenbugs. If anything, it might take a lot more skill to get a decade plus uptime using the kinds of stuff I see popular in microservices. Maybe.

                                                                                  So far, it’s easier to get high availability and security out of a centralized architecture with strongly-consistent replication to nearby datacenters. That’s the status quo. I’m still waiting to see any data similarly showing developers easily get high-availability out of microservices, esp on commodity components and distributed. Those are what they claim they can do it with. I’ve seen some write-ups where people talk about doing microservices right. Lots of companies doing mission-critical stuff on them. There’s probable already examples to be found of some that go years without downtime or severe issues in performance. We’ll get more data coming in over time as techniques mature. I’m interested in whatever people have.

                                                                              2. 3

                                                                                I think there’s a deeper reason. Decentralization just costs more. The concept is partially captured by the notion of ‘economies of scale.’ More to your point, marketing reduces discovery costs. Interesting to note that even without overt marketing, products, ideas (or any other good) tend to centralize due to ‘word of mouth.’

                                                                                1. 1

                                                                                  Yes I think the root reason is capitalism, and I don’t mean that in a boogie monster kind of way. Decentralized systems spread your risk if you have secrets in your business process. However in non-capitalist systems, like p2p the costs get reduced by decentralization because the burden isn’t shared by an individual but rather small contributions.

                                                                                  1. 2

                                                                                    “Decentralized systems spread your risk if you have secrets in your business process. “

                                                                                    They really don’t. This is a myth that might be worth a detailed write-up in the future. They can reduce risk in general but add risk by default. That’s because they turn one thing into several things plus their interactions. That automatically increases attack surface if we’re talking code or protocols. They often do a lot more with super-clever algorithms aiming for lots of desirable properties that get broken in new ways we’ve never seen before. Moreover, many of these run on the same CPU’s or OS’s that are commodities with the associated constant flow of 0-days. Depending on definition of risk, they might not reduce risk at all where three different organizations all using Windows or Linux can be compromised with one exploit in standard components. Then, they just directly reach for those secrets.

                                                                                    So, the truth is decentralized systems can reduce risk or increase difficulty on attackers if they have to break most of the components to defeat the system and there’s no shortcuts. That’s not often true given pentest results. If anything, it just takes extra time and money which the black hats going after business secrets have plenty of. So, you get increased chance of failure from decentralization with no meaningful increase in security in those cases. The benefits of decentralization should be assessed on case by case basis with assessor looking out for these common problems.

                                                                                    1. 1

                                                                                      It’s more basic than capitalism - it’s popularity. Note that the web is decentralized but players like FB and Twitter became large hubs so we see it as centralized. We could claim that it is an artifact of capitalism, but look at something like the original Twitter without their nudges. Some users become more popular than others and that leads them to get more followers. It turns Pareto.

                                                                                      The way that economics factors into is reduced discovery costs. For something like music, the urge is “I want to hear something good.” The stuff that other people think is good floods the system and makes it easier for you to find it. So, I’m not saying this is right or wrong, but rather that it is just something that happens. Favor your local independent coffee shop but as long as people can make choices something like Starbucks will rise as “the choice everyone knows.” There’s a centralizing tendency in systems.

                                                                                      1. 1

                                                                                        That’s not true and we’ve seen decentralized systems be wildly popular. Mastodon has over a million users right now.

                                                                                        1. 2

                                                                                          I’m willing to bet that the distribution of users over mastodon instances resembles a power law. It certainly isn’t flat. That’s my point. Popularity, whether money is involved or not, leads to hub-ishness and hubs are central to the nodes around them.

                                                                                          1. 1

                                                                                            Money accelerates that hubishness, without an exchange of money large instances must close their doors to new users after some point.

                                                                                            1. 1

                                                                                              I agree, but money is not only one way that value is accumulated and transferred. The basic thing that I’m talking about underlies both physical and social processes. The hierarchical structure of the veins on a leaf is a cost reduction maneuver too. It’s the same as the appearance of hub airports even though no money is involved. I discuss these ideas here.

                                                                            1. 4

                                                                              I’m not sure that this happens often enough to do anything. It could be an over-optimization. Sort of: trying to keep bad things from happening we might keep some good things from happening as well.

                                                                              1. 3

                                                                                The reason I raised my voice is just because I found the pattern problematic, I haven’t asked for moderator action or banning as some people seem to suggest here.

                                                                                What I ask people to do is maybe invest a little time on submission of such things, instead of going for the shock value.

                                                                              1. 7

                                                                                You can eliminate a lot of error handling by asking what you want to have happen under particular conditions, i.e., what’s the goal? Can it be met in another way?

                                                                                The reason libraries deal with error propagation issues is because they don’t know the context of their use. They don’t know what is supposed to happen when an “error” occurs.

                                                                                1. 2

                                                                                  Right. There are few things more frustrating as a library consumer than one which calls fprintf(3C), or worse, exit(2) on your behalf when it encounters something unexpected.

                                                                                1. 5

                                                                                  The good news is: Planck’s Constant puts a limit on the level of detail.

                                                                                  1. 8

                                                                                    High line coverage doesn’t necessarily imply covering enough of the state space. (Branch coverage is only slightly better in this regard.) As the post notes, testing with groups of input that correspond to different equivalence classes is more meaningful, because it exercises the code under test in several different ways.

                                                                                    There’s a problem hiding in plain sight, though: how often do we know all the edge cases?

                                                                                    Property-based testing, fuzzing, model checking, static analysis, and symbolic interpretation help here: Property-based testing and fuzzing do guided random exploration of the state space and report edge cases (known and unknown). Model checking, static analysis, and symbolic interpretation reason about possible behavior within the state space as a whole (possibly with some upper size bound).

                                                                                    1. 1

                                                                                      If you know a line is run during a test suite, it tells you nothing about whether it’s well tested.

                                                                                      However, if you know the line is not executed during a test suite, that does indeed tell you something about how well tested it is.

                                                                                      I don’t work towards 100% coverage - I work away from having code I know isn’t tested. It looks like the same thing from a distance, but it’s an important distinction to me.

                                                                                      1. 2

                                                                                        Absence of coverage can definitely be a useful insight. Line coverage can be an absence of evidence, but negative line coverage is evidence of absence. :) It just seems to lose something in translation when that coverage becomes a metric that triggers failures in CI, though.

                                                                                        (Also, if test input generated via property-based tests still aren’t covering parts of a program, it’s a sign the generator needs more variety.)

                                                                                      2. 1

                                                                                        I like to move this back to the root. Eliminate edge cases as much as possible.

                                                                                      1. 1

                                                                                        I wonder what would JavaScript be in this parallel world of programming languages characters :)

                                                                                        1. 3
                                                                                          1. 3

                                                                                            The Troubadour and his Travelling Troupe of Merry Dancers

                                                                                            Your songs about the Scripture are heard everywhere and are especially popular with younger children. Older people have long since given up trying to block you out of their villages.

                                                                                            1. 2

                                                                                              Spy from a distant land that the kingdom occasionally trades with. He is always browsing local windows and sending secrets back home. A bit slow, but nobody else was willing to live so far from home.