1. 10

    (Reposted from reddit.)

    This is not quite right.

    Parentheses describe a tree syntactically. Evaluation is something you can do to that tree, but it’s not the only thing.

    The linked post describes a mechanism for deriving a tree from a sequence of words—from the semantics of those words. But what’s interesting about s-expressions isn’t that they remind you of the arity or semantics of some symbol (so you don’t have to memorize it), but rather that they create a tree structure that exists wholly apart from any semantics.

    And this, I think, speaks volumes about the ideological differences between lisp and forth.

    1. 3

      ptrace […] The API is very ugly; it’s a single function which you provide with an enumerator value for what you want to do, and then some arguments which will either be used or ignored depending on which value you supply

      i-i-ioctl? After all these years—is it really you?

      1. 3

        I may sound bitter, but calling it “memory safety project” seems dishonest and intentionally misleading on the real goal: inject Rust in selected projects.

        I was expecting to see address sanitation, or use of theorem provers and separation logic, but it is not. 😒

        Also, take an fairly big project in Java (a language they deem to be memory safe), run Infer, count the amount of possible NullPointerException, then tell me how it fits the definition of memory safety they are using.

        1. 10

          then tell me how it fits the definition of memory safety they are using.

          There’s a significant, meaningful difference between Java NPE which always leads to program crashing with exception, and C-style undefined behavior, which sometimes leads to remote code execution. This difference exists regardless of definition being used.

          That being said, usefully defining what is “memory safety” is hard! The definition they are using is informal: absence of certain specific memory related bugs. NPEs are certainly memory safe according to their definition. If you get NPE, you don’t actually touch garbage memory.

          I don’t know what would be a more useful definition of memory safety. Formally, memory safety is just type safety, in a sense that “well-typed programs don’t get stuck”, but types&programming languages formalism is not directly translatable to real-world languages, which rarely have full formal semantics.

          1. 5

            Indeed. (Though I disagree with your assessment of java. ‘[D]efinition of memory safety’ is not at all difficult; it’s simple: java is sound. Barring the derived-array-upcast thing.)


            Let’s get the Rustls TLS library ready to replace OpenSSL in as many projects as possible.

            Please don’t. Use project everest’s libraries instead.

            1. 1

              @matklad and @Moonchild, thank you for your clarification on Java and NullPointerException; in fact, their concept of memory safety is restricted to out-of-bounds access and use-after-free, so Java fits their restricted definition of memory safety—but does not include most of the types described at Wikipedia article.

              @matklad, do you have a reference on the formal definition of memory safety?

              @Moonchild, I really liked you cited Project Everest, because when I opened this link, I expected to see exactly something like that. And that is where my disappointment (or “bitterness”, if one prefers) came from.

              1. 2

                The best discussion I know is in http://lucacardelli.name/papers/typesystems.pdf, “Execution errors and safety” section. The key idea is that runtime errors may be trapped or untrapped, and the language is safe if programs accepted by the compiler do not exhibit untrapped errors. That’s the difference between C++ and Java with respect to null pointers: Java traps all attempts at null pointer dereference, while C++ assumes that null pointer dereference can’t happen, and optimizes based on that.

            1. 3

              Okay, I can see the emoji thing going for the same approach as diacritics where we can have say ü written both as the ü glyph (U+00FC) or an u glyph (U+0075) followed by an umlaut ¨ (U+00A8).

              So, are we having emoji normalization forms too? Because I really don’t want to be the guy tracking sorting and filtering bugs that failed to match bear + snowflake sequences when searching for polar bears.

              1. 5

                It’s not really the same thing. Normalization happens because both the composed and decomposed versions result in, logically, the same string. There is no ‘composed’ equivalent for these sequences; the only way to represent a polar bear is to put multiple codepoints together.

                1. 1

                  No, but imagine you have a commutative case. E.G., what if snowflake+bear = bear+snowflake = polar bear? Then, what if a webpage contains snowflake+bear, but you search for bear+snowflake? You have to match the two somehow.

                  1. 6

                    Good news: modifiers and JWZ sequences aren’t commutative. [Snowflake, JWZ, bear] just looks like ❄️🐻.

                    1. 3

                      You mean ZWJ right?

                      1. 6

                        No, no, Jamie Zawinski has been really busy lately.

                        1. 2

                          Seriously. There were plenty of search results for “jwz unicode” that the “did you mean” thing did not kick in. it took me a bit to figure out the initialism was probably accidentally transposed.

                          1. 1

                            You’ve correctly guessed why I transposed it too.

                          2. 2

                            I, for one, am in favor of a JWZ unicode character. It would probably be disappointment/rage with technology.

                          3. 2

                            Yes

                    2. 1

                      The fun thing is that “are these two sequences the same emoji” becomes undecidable then :D

                    1. 5

                      Rationals would be a great default number type. When fractions are available you have to go way out of your way: Fraction(1, 10). Why shouldn’t 0.1 mean exactly one tenth?

                      Floats can have the ugly syntax: float(0.1). You’re rounding 1/10 to the nearest float.

                      1. 3

                        The problem is that only rationals which divide nicely by 10 have such nice syntax. For example, 1/3 cannot be written down in decimal point notation (as it would be 0.333 followed by an infinite number of threes). So, it makes more sense to use the fractional syntax for rational numbers and the decimal point syntax for floating-point numbers.

                        Of course, you can have your cake and eat it too: Lisps use exactly this syntax: 1/3 for rational numbers. It’s slightly ugly when you get larger numbers, because you can’t write 1 1/3. Instead, you write 4/3, appears rather unnatural. I think 1+1/3 would’ve been nicer and would have been consistent with complex number syntax (i.e. 1+2i). But it does complicate the parser quite a bit. And in infix languages you can’t do this because of the ambiguity of whether you meant 1/3 or (/ 1 3). But one could conceive a prefix syntax like r1/3 or so.

                        It’s unfortunate that the floating-point notation us humans prefer to use is base 10, while the representation in a computer is base 2, because these don’t divide cleanly, hence the weirdness of how 0.3 gets read into a float.

                        1. 5

                          Instead, you write 4/3, appears rather unnatural.

                          Unnatural? Nah. Maybe a bit improper, though.

                          1. 2

                            4/3 appears rather unnatural

                            Matter of opinion.

                            notation us humans prefer to use is base 10, while the representation in a computer is base 2, because these don’t divide cleanly, hence the weirdness of how 0.3 gets read into a float

                            Decimal formats are a thing. Supported by some databases and used for some financial work. Ultimately it doesn’t solve the problem of ‘I want to represent arbitrary fractions with perfect fidelity’. That being said you can go further in that direction by using a more composite number; neither 10 nor 2, but maybe 12.

                            in infix languages you can’t do this […] prefix syntax like r1/3 or so

                            Better solution: use a different separator. E.g. in j: 1r3.

                            1. 1

                              Decimal formats are a thing.

                              True, but I don’t know of any popular programming language which uses them as the native representation of floating point numbers.

                              It works well enough for raku.

                              How does it distinguish between a division operation on two numbers (which may well result in a rational number) and a rational literal?

                              1. 2

                                REXX?

                                1. 1

                                  As far as I know about Raku.

                                  When you write 0.3 in Raku, it is considered as a Rational and not a Float that is why 0.2 + 0.1 = 0.3 and division operator convert it also internally as a Rational (3/2).WHAT => Rat or (1/3).WHAT => Rat. Use scientific notation to create a double directly (the type will be Num). For arbitrary precision rational number, you will use FatRat type.

                                  Rational number in Raku from Andrew Shitov course

                                  Floating-point number in Raku from the same course

                          1. 22

                            A more serious problem: the text the browser shows when you hover over a link can’t be trusted. The website can swap out the href right as you click it. Google search does this, for instance.

                            For a demo, see here.

                            1. 3

                              Mod is always slow. How does this compare with using power-of-two-sized buffers and using and to mask indices?

                              1. 2

                                I imagine much faster, if you’re writing more than a byte at a time. Not only do you not have to switch to a slower mode when writing near the “end” of the buffer, but you also get to skip the logic to determine when to switch to slow mode.

                              1. 4

                                No comment on content, but nits while you’re here:

                                the use of brains have been avoided for that reason.

                                Should be ‘has’ (because it’s the use that’s been avoided, and ‘use’ is singular).

                                perhaps you might need that spectra

                                ‘Spectra’ is plural; go for ‘spectrum’ or ‘those’. (Also: more stylistic, but ‘perhaps’ and might’ is redundant.)

                                There are infinitely more rationals than integers

                                Rationals and integers have the same cardinality.

                                1. 3

                                  I do not understand linkers, but the post reminded me of this book Linkers and Loaders

                                  1. 3

                                    And it reminds me of John Levine mentioning in this book that the number of linker authors on this planet are just a handful (or something similar to that). No wonder, we just have this one book on the guts of linkers and loaders!

                                    1. 2

                                      I treat them as a black box and don’t understand them either, but this comment on the same article gave me a bunch of insight into it:

                                      https://news.ycombinator.com/item?id=27446341

                                      The analogy is that a linker (and an OS loader of shared libraries!) is like a compacting garbage collector – which I just wrote and spent awhile debugging, so it’s burned into my brain. My way of digesting this:

                                      • A garbage collector walks a graph of objects in memory starting from a live set / stack roots; a linker walks a function call graph, starting from main()
                                      • A compacting garbage collector produces a contiguous block of objects; A linker produces the contiguous executable (well at least the “text” portion of it).
                                      • GCs are conerned with data pages; linkers are concerned with code pages.
                                      • A garbage collector has to find edges within objects (there are several ways to do this); a linker has to find calls within functions
                                      • A leaf object has no pointers (e.g. Point {int x, int y}); a leaf function calls no others (abs() or something)
                                      • The Cheney GC algorithm is clever about the order in which objects are moved, so it can modify pointers correctly. The linker has the same issue with regard to adjusting code offsets
                                      1. 2

                                        I don’t think it’s fair to say a linker is like a garbage collector. A linker may have a garbage collector (and that is probably a good thing for linker to have), but its purpose is to resolve symbol references.

                                        1. 2

                                          It’s not about the linker having a garbage collector, but about the similarities in what a compacting gc and linker do. They both walk from a set of roots, trace, fix up, and move things around. The linker could also yeet dead code, but doesn’t have to. The isomorphism is the traversal.

                                    1. 6

                                      This article is amusing. Given sufficient information, yes, these guidelines myths, are incorrect.

                                          1. They are not exact
                                      

                                      Article claims False because they are exact within their capabilities. A sufficiently knowledgeable person can avoid the sharp edges like 0.1 and write safe code.

                                      Or just treat them as not exact.

                                          2. They are non-deterministic
                                      

                                      Article claims False. But you need to know the implementation details of your computation, including device designer, so you can treat them as deterministic.

                                      That was part of your perfect knowledge implant, right?

                                          3. NaN and INF are an indication of an error
                                      

                                      Article claims False. You expect them as safeguards for your perfect knowledge.

                                      1. 4

                                        All these points are the same: “It’s not true that [X]!” and then it continues to say “It is true that [X] under condition [Y]”. Ehm…

                                        Some decent enough content nonetheless though, and I think especially some junior programmers could benefit as “floats are not exact” is lacking in nuance. Just a shame of the contrarian clickbaity style. Don’t know who flagged it as spam, because it’s not really IMHO.

                                        1. 2
                                          2. They are non-deterministic
                                          

                                          They are 100% deterministic, IEEE specifies exact behaviour, and that does not vary between implementations. If you go into undefined behaviour (e.g. large values in the transcendentals) then your results may not be great, but the same is true of integers.

                                          3. NaN and INF are indication of an error
                                          

                                          What? you’re doing arithmetic, I assume you know what arithmetic does? You don’t get NaN except whether arithmetic makes no sense (so arguably an error), but those errors are the same as you get doing the maths on paper. Similarly the places you get +/- infinity are 90% the same as real math. The occasions that it isn’t true are overflow, and that happens with integers as well.

                                          1. 4

                                            They are 100% deterministic, IEEE specifies exact behaviour

                                            This is 100% true if your compiler and architecture are 100% IEEE compliant. If anything has enabled things like -ffast-math or if you are targeting 32-bit x86 and not SSE (for example) then you may find that some steps of a calculation are not actually IEEE compliant. -ffast-math enables things like fused multiply-add operations, which are individually deterministic but will give different precision to separate multiple and add operations. Most of the math.h functions have different implementations on different platforms with varying performance/precision trade-offs, so just because the primitive operations are well-specified doesn’t mean that more complex things are.

                                            1. 1

                                              You don’t need fma if you don’t trust the compiler, a+b+c can produce different results depending on how the compiler chooses to add.

                                          2. 2

                                            I disagree. It’s just different levels of knowledge. This article is addressing the overcaution that can come with limited knowledge — like the child who’s learned “don’t touch knives: they’ll cut you!”, and now needs to learn that this is … not exactly a myth, but something you need to partly unlearn to make use of knives.

                                            The point about exactness is very important and helps you use floats optimally. JavaScript is a great example: it makes the language and runtime simpler not to have to use both int and float representations for numbers.

                                            The point about INF is a good one in some cases where you can use INF as a limiting value, like when finding a max/min. The fact that NaNs propagate is also valuable.

                                            The one about nondeterminism is less useful. Maybe it doesn’t belong here. I agree it’s good to assume that FP computations can in general vary in their LSBs for weird and unpredictable reasons.

                                            1. 1

                                              not exact/exact

                                              There are two interpretations of floating point numbers:

                                              1. They are inexact quantities which we perform exact operations on.

                                              2. They are exact quantities which we perform inexact operations on.

                                              The jury is still out on which of these is the best way to think about the problem, and I expect that it is domain-specific. To say ‘just treat them as not exact’ is overly simplistic; we have a choice of whether to consider our operations or values as inexact.

                                              1. 1

                                                I believe 2 is wrong in the article; IIRC for some operations, the IEEE standard allows for the hardware to choose to do a fast-math optimization (or not) depending on runtime things like thread contention, the need to cache values, stashing registers into the stack, etc, and there are (were?) architectures that had a runtime nondeterminism in the result, even on the same hardware/compiler combo.

                                                1. 1

                                                  No, ieee754 allows some functions to be inaccurate because correct rounding may not be possible. In principle this only impacts the transcendental functions, and the most widespread hardware implementation is x87 which turns out to be inaccurate outside of some range (I can’t recall what), and unintentionally inaccurate within the range. It doesn’t matter though as everyone uses software implementations as they work with floats and doubles (x87 is ieee874 but uses an 80bit format so double rounding can happen), so much so that modern hardware doesn’t support them natively

                                                  1. 1

                                                    Yes, it was an x87 problem:

                                                    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323

                                                    Remember, Intel had a rep on the 754 committee and very likely got some sketchy stuff rolled into the spec to be sure that x87 was compliant. About midway through someone explains that the nondeterminism is allowed by the standard.

                                                    Anyways, even though it’s not “modern” there are plenty of things out there that are still using x87 so it may be important for someone to keep this in mind.

                                                    1. 1

                                                      Intel doesn’t deserve to be dissed here, it was intel that made ieee754 happen by demonstrating that you could actually implement it efficiently.

                                                      The price they paid for that was they predated the implicit leading 1, meaning the lost a bit of precision in fp80, as well as suffering from pseudo- denormals, infinities, and nans, as well as unnormals. They also ended up with fprem (remainder), and fprem1 (remainder according to ieee754).

                                                    2. 1

                                                      The reason no one uses x87 is because it’s dog slow, not because it’s inaccurate. There’s a huge market for low-accuracy high-bandwidth floating point operations.

                                                      correct rounding may not be possible […] transcendental functions

                                                      Isn’t this just a question of choosing branch cuts?

                                                      1. 1

                                                        There are many reasons not to use x87. So many reasons. Scientific computing love it though as for them the extra precision matters (at least in a cargo cult fashion). Fun story because win64 defined long double == double specfp isn’t valid for comparisons between x86_64 systems, due to the use of long double in some tests. It also invalidates comparisons to arm (again no 80bit arithmetic)

                                                        Some transcendental functions round differently at the same precision depending on how far out you evaluate. There are other things that impact values like what polynomials you use in the approximation, etc

                                                1. 2

                                                  Similarly cm used to resolve, though no longer apparently.

                                                  1. 2

                                                    UNIX uses the term file descriptor a lot, even when referring to things which are clearly not files, like network sockets

                                                    Nit: unix uses the term ‘file’ a lot, including to refer to things that are not persistent, hierarchically-accessed records, like network sockets.

                                                    1. 1

                                                      From my understanding, a file in Unix’s sense is essentially a stream of bytes.

                                                      1. 1

                                                        The term is not entirely well-defined (which is part of the problem). I think that, at the very least, a file encompasses a stream of bytes, but there are files which are not simply streams of bytes. Anything that can be ioctled, for instance.

                                                    1. 2

                                                      This was interesting and helpful for understanding for me, but I did not see anything about technical debt. Is there going to be another article in a series or something?

                                                      1. 2

                                                        The fact that there are multiple APIs for concurrent I/O is the technical debt.

                                                        1. 1

                                                          TFA seems to suggest that kevent/kqueue has more technical debt than epoll, so I don’t think that is the argument being made.

                                                          1. 5

                                                            The article author is biased. It’s easy enough to take a step back from their particular perspective and note that all of these multiple APIs are bad, and further that the forced choice of API is onerous for the programmer.

                                                            1. 1

                                                              What does a good API for concurrent IO look like, in your opinion?

                                                              1. 2

                                                                A few years ago, I would have said that a good concurrent I/O API should have two methods:

                                                                • Enqueue an action to occur in a fresh isolated concurrency domain, and return a handle for completing or cancelling the action
                                                                • Given some number of seconds, create a fresh handle which will complete after that time has elapsed

                                                                An example of this API in the real world is in Twisted Python, where the IReactorTime interface contains equivalent methods. This is the main low-level interface which is used to build concurrent applications with Twisted.

                                                                In the Spectre era, though, we need to privilege that second method; it shouldn’t be primitive. So, the core of the API is a single method which postpones actions until after the current computation has finished, optionally scheduling mutually-isolated actions to run simultaneously.

                                                                Now, let’s look at some technical debt. Twisted Python’s I/O core contains thirteen different implementations of an API which includes special methods for signals, sockets, timers, and file descriptors, since those are different on many different platforms. Integration with GUI libraries is ad-hoc and must be reinvented for each library. Windows support requires platform checks throughout the code; the select() wrapper used to be its two modules (Windows and non-Windows) and I don’t know if it’s more or less readable when currently sewn together into a single module.

                                                                Given this history, it should now be understandable why the original author praises Linux for a unified file-descriptor approach. It’s less code for high-level networking libraries to maintain!

                                                                1. 1

                                                                  Thanks! Why do you think Spectre means we need to privilege that second method?

                                                                  1. 1

                                                                    Spectre is all about timer abuse. If we can reduce the ability of code to measure its own time taken to execute, then we can reduce the chances of introducing Spectre bugs.

                                                      1. 26

                                                        These are all valid criticisms of certain patterns in software engineering, but I wouldn’t really say they’re about OOP.

                                                        This paper goes into some of the distinctions of OOP and ADTs, but the summary is basically this:

                                                        • ADTs allow complex functions that operate on many data abstractions – so the Player.hits(Monster) example might be rewritten in ADT-style as hit(Player, Monster[, Weapon]).
                                                        • Objects, on the other hand, allow interface-based polymorphism – so you might have some kind of interface Character { position: Coordinates, hp: int, name: String }, which Player and Monster both implement.

                                                        Now, interface-based polymorphism is an interesting thing to think about and criticise in its own right. It requires some kind of dynamic dispatch (or monomorphization), and hinders optimization across interface boundaries. But the critique of OOP presented in the OP is nothing to do with interfaces or polymorphism.

                                                        The author just dislikes using classes to hold data, but a class that doesn’t implement an interface is basically the same as an ADT. And yet one of the first recommendations in the article is to design your data structures well up-front!

                                                        1. 14

                                                          The main problem I have with these “X is dead” type article is they are almost always straw man arguments setup in a way to prove a point. The other issue I have is the definition or interpretation of OOP is so varied that I don’t think you can in good faith just say OOP as a whole is bad and be at all clear to the reader. As an industry I actually think we need to get past these self constructed camps of OOP vs Functional because to me they are disingenuous and the truth, as it always does, lies in the middle.

                                                          Personally, coming mainly from a Ruby/Rails environment, use ActiveRecord/Class to almost exclusively encapsulate data and abstract the interaction with the database transformations and then move logic into a place where it really only cares about data in and data out. Is that OOP or Functional? I would argue a combination of both and I think the power lies in the middle not one versus the other as most articles stipulate. But a middle ground approach doesnt get the clicks i guess so here we are

                                                          1. 4

                                                            the definition or interpretation of OOP is so varied that I don’t think you can in good faith just say OOP as a whole is bad and be at all clear to the reader

                                                            Wholly agreed.

                                                            The main problem I have with these “X is dead” type article is they are almost always straw man arguments setup in a way to prove a point.

                                                            For a term that evokes such strong emotions, it really is poorly defined (as you observed). Are these straw man arguments, or is the author responding to a set of pro-OOP arguments which don’t represent the pro-OOP arguments with which you’re familiar?

                                                            Just like these criticisms of OOP feel like straw men to you, I imagine all of the “but that’s not real OOP!” responses that follow any criticism of OOP must feel a lot like disingenuous No-True-Scotsman arguments to critics of OOP.

                                                            Personally, I’m a critic, and the only way I know how to navigate the “not true OOP” dodges is to ask what features distinguish OOP from other paradigms in the opinion of the OOP proponent and then debate whether that feature really is unique to OOP or whether it’s pervasive in other paradigms as well and once in a while a feature will actually pass through that filter such that we can debate its merits (e.g., inheritance).

                                                            1. 4

                                                              I imagine all of the “but that’s not real OOP!” responses that follow any criticism of OOP must feel a lot like disingenuous No-True-Scotsman arguments to critics of OOP.

                                                              One thing I have observed about OOP is how protean it is: whenever there’s a good idea around, it absorbs it then pretend it is an inherent part of it. Then it deflects criticism by crying “strawman”, or, if we point out the shapes and animals that are taught for real in school, they’ll point out that “proper” OOP is hard, and provide little to no help in how to design an actual program.

                                                              Here’s what I think: in its current form, OOP won’t last, same as previous form of OOP didn’t last. Just don’t be surprised if whatever follows ends up being called “OOP” as well.

                                                          2. 8

                                                            The model presented for monsters and players can itself be considered an OO design that misses the overarching problem in such domains. Here’s a well-reasoned, in-depth article on why it is folly. Part five has the riveting conclusion:

                                                            Of course, your point isn’t about OOP-based RPGs, but how the article fails to critique OOP.

                                                            After Alan Kay coined OOP, he realized, in retrospect, that the term would have been better as message-oriented programming. Too many people fixate on objects, rather than the messages passed betwixt. Recall that the inspiration for OOP was based upon how messages pass between biological cells. Put another way, when you move your finger: messages from the brain pass to the motor neurons, neurons release a chemical (a type of message), muscles receive those chemical impulses, then muscle fibers react, and so forth. At no point does any information about the brain’s state leak into other systems; your fingers know nothing about your brain, although they can pass messages back (e.g., pain signals).

                                                            (This is the main reason why get and set accessors are often frowned upon: they break encapsulation, they break modularity, they leak data between components.)

                                                            Many critique OOP, but few seem to study its origins and how—through nature-inspired modularity—it allowed systems to increase in complexity by an order of magnitude over its procedural programming predecessor. There are so many critiques of OOP that don’t pick apart actual message-oriented code that beats at the heart of OOP’s origins.

                                                            1. 1

                                                              Many critique OOP, but few seem to study its origins and how—through nature-inspired modularity—it allowed systems to increase in complexity by an order of magnitude over its procedural programming predecessor.

                                                              Of note, modularity requires neither objects nor message passing!

                                                              For example, the Modula programming language was procedural. Modula came out around the same time as Smalltalk, and introduced the concept of first-class modules (with the data hiding feature that Smalltalk objects had, except at the module level instead of the object level) that practically every modern programming language has adopted today - including both OO and non-OO languages.

                                                            2. 5

                                                              I have to say, after read the first few paragraphs, I skipped to ‘What to do Instead’. I am aware of many limitations of OOP and have no issue with the idea of learning something new so, hit me with it. Then the article is like ’hmm well datastores are nice. The end.”

                                                              The irony is that I feel like I learned more from your comment than from the whole article so thanks for that. While reading the Player.hits(Monster) example I was hoping for the same example reformulated in a non-OOP way. No luck.

                                                              If anyone has actual suggestions for how I could move away from OOP in a practical and achievable way within the areas of software I am active in (game prototypes, e.g. Godot or Unity, Windows desktop applications to pay the bills), I am certainly listening.

                                                              1. 2

                                                                If you haven’t already, I highly recommend watching Mike Acton’s 2014 talk on Data Oriented Design: https://youtu.be/rX0ItVEVjHc

                                                                Rather than focusing on debunking OOP, it focuses on developing the ideal model for software development from first principles.

                                                                1. 1

                                                                  Glad I was helpful! I’d really recommend reading the article I linked and summarised – it took me a few goes to get through it (and I had to skip a few sections), but it changed my thinking a lot.

                                                                2. 3

                                                                  [interface-based polymorphism] requires some kind of dynamic dispatch (or monomorphization), and hinders optimization across interface boundaries

                                                                  You needed to do dispatch anyway, though; if you wanted to treat players and monsters homogenously in some context and then discriminate, then you need to branch on the discriminant.

                                                                  Objects, on the other hand, allow interface-based polymorphism – so you might have some kind of interface […] which Player and Monster both implement

                                                                  Typeclasses are haskell’s answer to this; notably, while they do enable interface-based polymorphism, they do not natively admit inheritance or other (arguably—I will not touch these aspects of the present discussion) malaise aspects of OOP.

                                                                  1. 1

                                                                    You needed to do dispatch anyway, though; if you wanted to treat players and monsters homogenously in some context and then discriminate, then you need to branch on the discriminant.

                                                                    Yes, this is a good point. So it’s not like you’re saving any performance by doing the dispatch in ADT handling code rather than in a method polymorphism kind of way. I guess that still leaves the stylistic argument against polymorphism though.

                                                                  2. 2

                                                                    Just to emphasize your point on Cook’s paper, here is a juicy bit from the paper.

                                                                    Any time an object is passed as a value, or returned as a value, the object-oriented program is passing functions as values and returning functions as values. The fact that the functions are collected into records and called methods is irrelevant. As a result, the typical object-oriented program makes far more use of higher-order values than many functional programs.

                                                                    1. 2

                                                                      Now, interface-based polymorphism is an interesting thing to think about and criticise in its own right. It requires some kind of dynamic dispatch (or monomorphization), and hinders optimization across interface boundaries.

                                                                      After coming from java/python where essentially dynamic dispatch and methods go hand in hand I found go’s approach, which clearly differentiates between regular methods and interface methods, really opened my eyes to overuse of dynamic dispatch in designing OO apis. Extreme late binding is super cool and all… but so is static analysis and jump to definition.

                                                                    1. 2

                                                                      However, this is a double edged sword when it comes to C++; because everyone includes the standard library (even if you don’t include it, your includes will), and because C++’s template system means that everything that’s templated has to include the definitions in the header as well, this means that anytime someone touches AK in a commit, the entire operating system has to be rebuilt (~3400 files at the time of writing).

                                                                      Amusingly, Common Lisp has a similar issue - if you redefine a macro, you have to manually recompile all of the other macros and functions that instantiate it.

                                                                      1. 1

                                                                        Imagine if lisp implementations allowed you to use late bindings for macros, too, triggering an eval on every application of one. Performance would suffer, but it would be hella convenient for this use case.

                                                                        1. 1

                                                                          Well, the solution that I had in mind was for Lisp implementations to keep a dependency graph of functions and macros, and automatically recompile out-of-date functions and macros when the macros that they depended on were updated. This might be the same as what you’re suggesting, but I’m not sure.

                                                                          1. 1

                                                                            I meant that you would transform something like

                                                                            (defmacro m (...)
                                                                              ...)
                                                                            (defun f (...)
                                                                              (m ...))
                                                                            

                                                                            into

                                                                            (defun f (...)
                                                                              ...)
                                                                            (defun f (...)
                                                                              (eval (m '...)))
                                                                            

                                                                            That is, every time you evaluate code that uses a macro, you re-evaluate the macro.

                                                                      1. 2

                                                                        Look at K and Mathematica (aka Wolfram) for two languages that integrate the ideas from APL and Lisp in a more interesting way. In these languages, multidimensional arrays are represented by nested lists, and all scalar operations are generalized to operate on lists. This is a powerful idea whose benefits go well beyond the linear algebra implemented by LispE.

                                                                        The idea is most powerful if you have only one kind of list (like in K). So a character string is just a list of characters. A matrix is a list of lists of numbers. Instead of representing bit strings as integers (as LispE does with the bitwise operators), a bit string is a list of booleans. All of your scalar operations (on numbers, booleans, and characters) are generalized to work on lists.

                                                                        1. 1

                                                                          Do you have any good resources for learning K? I have a few bookmarked here but in everything I’ve found there seems to be a lack of practical exercises/projects so I end up forgetting everything I read.

                                                                          1. 1

                                                                            I’m not sure if any of my resources are “good”. Another problem is that there are many dialects of K.

                                                                            1. 1

                                                                              Thanks. The last one is the one I’m trying to follow this time.

                                                                            2. 1

                                                                              K crash course was good. It refers to k7, which is no longer current, though it can still be run via web.

                                                                              Q for mortals is decent, though very slow.

                                                                              1. 1

                                                                                I believe this is the same as https://github.com/kparc/kcc

                                                                                1. 1

                                                                                  Latter is incomplete, in the process of being updated to k9.

                                                                                  1. 1

                                                                                    Oh, good to know.

                                                                            3. 1

                                                                              LispE operates on tensors, which are implemented as nested lists.

                                                                              1. 2

                                                                                Tensors do not an apl make. Tensors, a concise notation, a rank operator…


                                                                                In apl, everything is an array, and functions operate homogenously on the same. Power emerges from that.

                                                                                In lisp, everything is an atom or a pair of things, and functions operate homogenously on the same. Power emerges from that, too.

                                                                                Newer lisps add things like arrays, but an array is little more than a concession to performance; its role is similar to that of the cons: a container for other objects. Lisp’s language is abstraction, and it speaks it well.

                                                                                When you try to blend the two, you end up with something that is neither a very good lisp nor a very good apl. (It may be good at being something else. I do not mean to make a value judgement.) Tensors feel more scalar than lisp’s aggregates and more aggregate than lisp’s scalars, and the homogeneity of each language is lost.

                                                                                1. 1

                                                                                  Actually, you should have a look on how lists are implemented in LispE (see https://github.com/naver/lispe/blob/master/include/listes.h). They are implemented as arrays, not as lists. I worked with many versions of Lisp over the years, and I had an issue with the idea of pushing elements at the beginning of the list, which is usually quite unnatural in most languages. I opted instead to a different implementation which is composed of two layers: ITEM (the array implementation) and LISTE, which contains a pointer to an ITEM. LISTE also contains an index to the first element in the array. A CDR for instance, creates a LISTE object that shares the same ITEM with the initial list, but move its index one notch over.

                                                                                  1. 1

                                                                                    I don’t see why something’s being unnatural in one language should have any bearing on that thing’s role in another language.

                                                                                    Which being said, you can push elements to the end of a list, by keeping a pointer to the final cons. Another pattern which is very common is to add elements to the head of a list and reverse it once done.

                                                                                    That’s all mostly beside the point, though; again, the semantic role of arrays in lisp is almost identical to that of conses in this context.

                                                                                    1. 1

                                                                                      You may be interested in gamelisp for a different take on how to do a “lisp” with a vector rather than cons cells.

                                                                              1. 3

                                                                                Eh…the only change I’ve noticed is that, when switching vdesktops, firefox hangs for a moment on a stale frame before recovering. Sometimes it even requires explicit interaction before flushing the stale frame.

                                                                                1. 2

                                                                                  Slightly OT:

                                                                                  Sometimes it even requires explicit interaction before flushing the stale frame.

                                                                                  I experience this quite often with Firefox stable and nightly on Android 11 and 10.

                                                                                1. 9

                                                                                  As much as I love and respect (I should, since I wrote a small part of the C library) the enthusiasm Andreas has, I kinda wish Serenity would dabble with different ideas/research. But that’s just a hobbyist OS development thing in general - everyone races to implement POSIX.

                                                                                  1. 14

                                                                                    everyone races to implement POSIX.

                                                                                    it opens the door for a lot of software, so I can see why people do that

                                                                                    1. 9

                                                                                      Yeah, the dopamine from having gcc run on your OS is obvious. I just wish other stuff happened too.

                                                                                      1. 8

                                                                                        Interestingly enough, I’m only aware of a few “Operating Systems Specifications” that could be implemented:

                                                                                        • POSIX/SUS (and its various profiles/derivatives/antecedents/related specs)
                                                                                        • OpenStep
                                                                                        • Windows 3.1 (ECMA-234)
                                                                                        • TRON
                                                                                        • Various avionics/maritime/motor vehicle specs
                                                                                        • You could call (U)EFI an “operating system specification” but that’s not really its intended goal

                                                                                        Obviously there are a lot of •de facto* OS standards. The above is purely those standards recognized as endorsed by a standards body.

                                                                                        If anyone else knows of any OS standards, I’d love to hear about them.

                                                                                        1. 19

                                                                                          No one said you had to implement a spec. In fact, a hobby OS without the commercial imperatives to implement a standard frees you of much of that, unless you want to explicitly run the morass of POSIX programs out there. Mix what you like or make new things.

                                                                                          SUS

                                                                                          Also, I hate that I’m doing this on this website, but I can’t resist:

                                                                                          1. 1

                                                                                            thank you, it was hard for me to fight the temptation

                                                                                          2. 5

                                                                                            Unix isn’t just attractive because of its compatibility. It’s also:

                                                                                            • fairly simple—not that hard to implement on your own

                                                                                            • familiar; it’s hard to come up with new paradigms

                                                                                            • still in common use; how much new, real-world software still supports other specifications you mention?

                                                                                            1. 4
                                                                                              • Windows 3.1 (ECMA-234)

                                                                                              I hadn’t heard that there had been a standard written up for that! I love that ECMA make it easy to get the standards text, unlike some bodies (cough fucking ISO coughcough)

                                                                                              • TRON

                                                                                              I hadn’t heard of this either and now I’m slightly upset because there’s a whole family of stuff there that I’d been totally unaware of.

                                                                                              What a pain in the neck to Google for, though. Blame Disney.

                                                                                              First really interesting detail that jumps out to me on http://www.ecos.sourceware.org/docs-1.3.1/ref/ecos-ref.a.html is this:

                                                                                              In particular, the default configuration has time slicing (also known as round-robin scheduling) switched on; this means that a task can be moved from RUN state to READY state at any time, in order that one of its peers may run. This is not strictly conformant to the µ ITRON specification, which states that timeslicing may be implemented by periodically issuing a rot_rdq(0) call from within a periodic task or cyclic handler;

                                                                                              So the ITRON standard explicitly requires cooperative multitasking rather than preemptive. At first blush I would kind of expect that to rule out multiprocessing?

                                                                                              It’s amusing to see every function name I saw mentioned on that page is either 7 or 8 characters long. Reminds me of the ANSI C 6 characters thing. https://stackoverflow.com/questions/38035628/c-why-did-ansi-only-specify-six-characters-for-the-minimum-number-of-significa

                                                                                        2. 5

                                                                                          POSIX has created a humongous social gravity well, if not a black hole at this point: POSIX is very widely taught and gives developers (at least subjectively) a simple and understandable shared view of computing. OSes are more than just implementations of their basic concepts, OSes are also their users, their community and their worldview, the sum of their experience and time invested in understanding (and human attention is very expensive).

                                                                                          Sadly, the more original a hobby OS is, the less popular it will be. SerenityOS is a good case in point: it has never tried to be original in its concepts, it is explicitly optimized for familiarity and reimplementing well-known and popular concepts (POSIX and Windows UI) using well-tested tools (C++) while doing this with character, taste and coherence.

                                                                                          1. 5

                                                                                            I think you kinda have to implement POSIX – even Windows has done so on multiple occasions. But that doesn’t preclude implementing new paradigms as well.

                                                                                            I would like to see something a lot better in terms of security and process-level virtualization (containers). I think it makes sense to do that on top of something POSIX-ish so you have some application code to test it out on. Otherwise I think you will be left with a lot of toy applications on your OS that don’t really give it a workout.

                                                                                            I’d also like to see more introspection, tracing, and performance monitoring along the lines of eBPF.

                                                                                            Basically all the “cross-cutting concerns”, which Linux is bad at, or at least has very messy bolted-on designs for.

                                                                                          1. 47

                                                                                            TeX is a really strange program by modern standards. One of my favorite tidbits about it is that you can launch external programs and read their output, but that facility works by writing the command line to a magic-number file descriptor and then reading from that file descriptor.

                                                                                            I have been whatever the opposite of a fan is of LaTeX for a long time. I learned ConTeXt because I got sick of the magic libraries, noisy syntax and general ugliness of LaTeX. I think a lot of computer scientists and programmers hold LaTeX’s output in higher regard than it deserves, because Knuth is a genius and studied typography and Lamport’s a genius and invented LaTeX or whatever. But the defaults for LaTeX are hideous to anyone with eyes and Knuth’s fonts are not amazing outside math.

                                                                                            ConTeXt absolutely makes more sense than LaTeX. If you need a powerful facility for generating quality PDFs for print, like the idea of using TeX and the cost, definitely check it out. A downside of ConTeXt is that it sort of assumes that you really know what’s going on with TeX for deeper customizations. This sent me down a rabbit hole with TeX.

                                                                                            The basic ideas of TeX are definitely interesting and powerful. The problems (in my opinion) are twofold: first, the primitives you have access to just don’t seem to be powerful enough for a lot of modern problems, and second, you’re a bit hamstrung by the design constraints of the system itself. Regarding the former, (for instance) it’s rather difficult to set things up so that TeX figures out the sizes things should be, but also is able to retain those sizes across page boundaries. TeX wants to create boxes and fill them in, and it’s got a separate asynchronous thing that ships out pages when it has enough boxes to fill them. Communicating between those facilities is tricky. This makes things like facing-translation hard. At the same time, the macro facility in plain TeX, unconstrained by LaTeX’s conventions, is much better for human readability.

                                                                                            For efficiency reasons in the early modern era, TeX wants to process the document only in a forward direction. This leads to the second problem. There’s no way (in one pass) to communicate backwards to earlier pages and there is no data structure in memory representing the document as a whole. This is why it takes many passes to generate a LaTeX document: each reference’s location gets recorded in the first pass, and on the next pass LaTeX can read the file with the references to generate the appropriate text. Of course, when a reference changes from “(?)” to “(Subrahiman, Mellish, Gazdar et. al 1977)”, that can change how many boxes fit on the page, so that can necessitate another pass. Having to do work in multiple passes is so common in TeX that there’s a primitive operation for creating a side file and writing commands into it.

                                                                                            The mouth/gullet/stomach thing has to do with macro expansion and what knowledge TeX will let you have and when. The process overall has an intriguingly asynchronous flavor, that it’s in horizontal mode building this line, then enters vertical mode building these paragraphs, and eventually has to engage the page shipper because you have too many paragraphs. But while I’m sure that it was easy for Knuth to keep all this straight in his head, it’s quite unstraightforward for mortals. And of course it makes machine-processing of TeX source by anything other than TeX nearly impossible.

                                                                                            Part of the popularity of LuaTeX is that it makes it really easy to do things that, while possible with plain TeX, would be incredibly onerous because it just isn’t a modern programming language. I had a macro for LuaTeX that would take some SQL, run it against a database, and then emit syntax highlighted SQL and a nicely formatted table with the real result of running that SQL. This entailed loading a common Lua library and a common TeX library for formatting code, and just sort of gluing it all together with Lua, it was simple. Conversely, the most complex thing I’ve ever tried to do with plain TeX is probably to make a résumé with colored rules and real fonts. Having a real modern language with real data structures at your disposal helps immensely.

                                                                                            Finl is not the first bold TeX replacement I’ve heard of; lout comes to mind, and got pretty far along before it got abandoned. There are so many hard parts to this problem: dealing with PDF, fonts, page layout in full generality, math. It seems like usually one of them is fatal to a new approach. I’m often tempted to give up on this as a concept and just buy Prince XML or just say everything’s going to be HTML from now on. But sometimes you just want to see a good-looking drop cap, or you need to give someone a PDF or something. Anyway, I wish the author well on this extremely long and complex journey!

                                                                                            1. 28

                                                                                              Of course, when a reference changes from “(?)” to “(Subrahiman, Mellish, Gazdar et. al 1977)”, that can change how many boxes fit on the page, so that can necessitate another pass.

                                                                                              Many years ago, I found (and have since lost) a .tex file someone designed to never converge, because the references would dance between page boundaries.

                                                                                              1. 28

                                                                                                It is well you lost this cursed object.

                                                                                                1. 8

                                                                                                  Good news! I found it again: https://tex.stackexchange.com/a/79699

                                                                                                2. 9

                                                                                                  Not quite the same, but I’ve had something similar happen on the web sometimes. Hovering over an element will cause it to move out from under the mouse, following which it will move back, so it would jump back and forth and flicker indefinitely.

                                                                                                  1. 0

                                                                                                    I find that so aggravating!

                                                                                                3. 9

                                                                                                  Reading the TeX source code is a treat: http://brokestream.com/tex.pdf

                                                                                                  It is probably the only piece of software that’s not trivial yet is laid out so logically that I can figure out how something is done in:

                                                                                                  1). A language I don’t actually know.

                                                                                                  2). Don’t really understand.

                                                                                                  3). Am not a subject matter expert.

                                                                                                  4). Have no idea where to start.

                                                                                                  In under 20 minutes. I’ve worked on code bases for months where I would still not be half as productive.

                                                                                                  1. 4

                                                                                                    This comment was more useful than the original post and the fact that, as of this writing, the comment has more upvotes than the story, I think others agree. Thank you for this!

                                                                                                    1. 3

                                                                                                      Thanks! Glad it was useful to someone!

                                                                                                    2. 3

                                                                                                      I agree mostly but the argument for ConTeXt does not convince me. ConTeXt to LaTeX is pretty much Ruby to Perl. Neither provides a better solution. As I have most of the scripts in Perl already, I am not going to touch Ruby. I will wait until Python comes along and all the cool kids started writing all kinds of scripts, so much so that I can pip anything and forgets about cpan. Until then, I’ll stick to LaTeX.

                                                                                                      1. 1

                                                                                                        Yes, the greater your investment in LaTeX, the more difficult a switch would be. I’m fortunate in that all of my projects are pretty independent and I don’t do them very frequently, so I could afford to make these deep dives for my own edification. Most people who are using LaTeX day-to-day are compelled to do so by journals or institutional requirements and don’t have the luxury to switch to ConTeXt. Even if you have the option, I can respect not wanting to deal with a new system, especially if you have a lot of built-up knowledge about how to solve your problems with LaTeX packages. The package ecosystem everywhere else is much smaller.

                                                                                                    1. 4

                                                                                                      ConTeXt doesn’t seem to be included in this analysis of TeX engines & macro packages?

                                                                                                      1. 5

                                                                                                        I agree that in principle ConTeXt is probably the best situated of the various TeXans. But TFA doesn’t really give any analysis to speak of of anything aside from TeX and LaTeX, except to say ‘fragmentation bad’.