Live data from Hacker News

In defense of complicated programming languages

viralinstruction.com

251–260 of 379 posts

Re: In defense of complicated programming languages

#251

Earlier quoted context omitted.

I agree with your general principle but I do think infix notation is still better because so many equations are a tree of binary operators. Look at how binary trees are drawn - the nodes are in the middle of their children. It just makes sense for binary operators to have their operands on either side. Otherwise you end up having to maintain some kind of mental stack which is just a bit mentally taxing.

There is something I am missing here. How does your mental model change that much from where the operator goes? Can't you put node to right of a column of children? Like you would do on a piecewise function. I am dislexic, maybe that's why I do not see your point.

Prefix and postfix notations permit a great distance between the operands and the operators. If done in a disciplined fashion so that they're merely swapped (at least in the majority of cases), then sure, it's not too different. But consider this: (¬ used for unary negation)

  b ¬ b 2 ^ 4 a c * * - √ + 2 a * /
The two expressions being divided are quite large, so there's no easy way to just move the division one token to the right, it goes all the way to the end. Which two things are being divided at a glance, just point them out. I've even selected a more familiar expression for this exercise that should make it easier. But what about an expression people are less familiar with? Which thing are we calculating the square root of? This is what RPN looks like when entered into a calculator, which is handy and quick for computations (I mean, I have an HP-32S II at my desk next to my laptop, I like it). But even the HP-48G with its graphical equation editor didn't force you to think like this, algebraic notation provides locality of information that makes it much more comprehensible without overburdening your working memory.

And once you start adding parentheses or indentation to convey that information, you're on your way back to a tweaked infix notation. The same for prefix notations. If you can see past the parentheses of Lisp/Scheme it's not too hard to see what's being expressed (using the same symbols as above):

  (/ (+ (¬ b)
        (√ (- (^ b 2)
              (* 4 a c))))
     (* 2 a))
This is more comprehensible, but it's not a strict prefix notation anymore, we've used parentheses and indentation in order to convey information about the grouping. I've even borrowed Lisp's use of variable numbers of arguments to simplify *. If asked, "What is being divided by what", you can, rather quickly, identify the divisor and dividend, at least point them out if not express totally what they do. But a straight prefix notation or postfix notation makes that task very difficult.

You could start naming subexpressions, but then you get into one of the two hard problems in computer science (naming things).

And then we get to the other things people want to do with mathematical expressions, which is largely symbolic manipulation. The present infix notation (or a highly demarcated prefix/postfix notation like the Lisp-y one above) is much better for this task than a strict prefix/postfix notation.

Re: In defense of complicated programming languages

#252
post #157

Earlier quoted context omitted.

How is encapsulation thrown out the window in sizeable codebases?? It is the single most important thing OOP gives, and is used by the majority of all programmers and has solid empirical evidence for its usefulness.

Could I see some examples of this evidence? Let's consider a really simple object graph: A -> B C A holds a reference to B, C has no reference to other objects. A is responsible for Bs state. By the principles of encapsulation, B is part of As state. What if it turns out later, that C has business with B? It cannot pass a message to A, or B. So, we do this? A -> B Wait, no, we can't do that, because then B would be p…

Those are called callbacks, we use those everywhere. If you don't want to use callbacks, you can use something like a publish-subscribe pattern instead so that X doesn't need to be indirectly linked to B through A and can publish directly to X.

Re: In defense of complicated programming languages

#253

Earlier quoted context omitted.

I understand this thought process, but in my opinion it's the wrong way to think about software concepts. Understanding what a bridge is doesn't mean knowing how to build one, and in fact tying your understanding to a certain implementation of a bridge just limits your ideas about what is, in fact, an abstract concept. We understood functions as "mappings" between objects for hundreds of years, and when programming c…

I didn't understand how engines worked until I took them apart, either. I was taking things apart to understand them long before computers :-) But the notions of sending a "message" to a "method" just was way way too handwavy for me. I like examining the theory after I learn the nuts and bolts. > Understanding what a bridge is doesn't mean knowing how to build one If you don't know how to build one, you don't underst…

> But the notions of sending a "message" to a "method" just was way way too handwavy for me. I like examining the theory after I learn the nuts and bolts.

Ooh-- are we talking about Smalltalk here? Java?

C'mon, Walter. You can't tell half a ghost story and then say goodnight. What did you find staring back at you deep in the void of all that object-orientedness?

Or, if this is a famous ghost story you've told before at least give us a link. :)

Re: In defense of complicated programming languages

#254
post #117

Earlier quoted context omitted.

How does a short learning curve necessarily correlate to a "nearly empty" toolbox? That seems like a fallacy to me. Ruby is fairly easy to learn. Does that mean it has a nearly empty toolbox? No it doesn't. A language with a GC is easier to learn than one without a GC. Does the one with a GC have less in the toolbox? What if it also allows opting out of GC? ObjC is considered a hard language to learn, esp for people…

> Ruby is fairly easy to learn No, its not. Ruby as a language is about as complex as they come. It is easy to get to a productive state though, but that is not the same thing as having learned the language. Length of learning curve and accessibility are vastly different things.

Python is the same. It's easy to write simple code, but once you get deep enough, the skeletons come out of closet.

Re: In defense of complicated programming languages

#255
post #233
post #211

Earlier quoted context omitted.

If you don't have any imagination and you treat dogs as database entries then yes you might do just that. If you are only person writing it and using that code, the same. You did not address interaction between dogs. Some other idea, how do you pass dogs to a playpen, how would they interact - maybe you want different playpens where you can send your dogs into, some playpens having food, some not having a food, diffe…

Well respectfully, you provided the original project spec as an example of where objects were required, and I demonstrated that no, they mostly weren't. I agree that it's unproductive for you to keep adding things to the spec so that I can demonstrate that nope, you still don't need objects to do all that. All that objects are, are functions glued to data. That's it. Whether or not you choose to glue them together, t…

Make Dog implement Healable, duh.

Re: In defense of complicated programming languages

#256

Earlier quoted context omitted.

I understand this thought process, but in my opinion it's the wrong way to think about software concepts. Understanding what a bridge is doesn't mean knowing how to build one, and in fact tying your understanding to a certain implementation of a bridge just limits your ideas about what is, in fact, an abstract concept. We understood functions as "mappings" between objects for hundreds of years, and when programming c…

I tend to agree with you in principle, but for me too, a lot of high-level features are better understood in term of translations. Objects, coroutine, closures... Even in formal CS, it's common to define the semantics of a language by translation, which can give more hindsight than operational semantics. Now that I think of it, I think the problem is that most languages are defined informally which can be imprecise a…

> The translation provided by the compiler is the closest thing we have to a formal semantics, it's natural to rely on it.

Which translation, though? Depending on your compiler flags, you may get very different translations, sometimes (if your program contains undefined behavior) even with dramatically different runtime effects.

Re: In defense of complicated programming languages

#257
post #224
post #135

Earlier quoted context omitted.

I'm curious to hear where you've seen Rust not scaling to large teams or codebases?

I'd also be interested in a Haskell example.

Generally this problem is because someone went crazy with template haskell or generics

Re: In defense of complicated programming languages

#258
post #119

Earlier quoted context omitted.

From the assembly one can learn what compilers do. But it cannot teach how modern CPU actually work. I.e. even with assembly reordering, branch prediction, register renames, cache interaction etc. are either hidden from code or exposed in a rather minimal way.

Right, in particular this is vital for Concurrency. In the 1990s my class about multi-tasking began by explaining that the computer can't really do more than one thing at a time. But in 2022 your computer almost certainly can do lots of things at the same time. And your puny human mind is likely not very well suited to properly understanding the consequences of that. What's really going on is too much to incorporate…

Then there is no much difference between learning C and assembly. Both are languages for an abstract machine that has less and less relation to how things are done for real.

Re: In defense of complicated programming languages

#259
post #157

Earlier quoted context omitted.

How is encapsulation thrown out the window in sizeable codebases?? It is the single most important thing OOP gives, and is used by the majority of all programmers and has solid empirical evidence for its usefulness.

Could I see some examples of this evidence? Let's consider a really simple object graph: A -> B C A holds a reference to B, C has no reference to other objects. A is responsible for Bs state. By the principles of encapsulation, B is part of As state. What if it turns out later, that C has business with B? It cannot pass a message to A, or B. So, we do this? A -> B Wait, no, we can't do that, because then B would be p…

Encapsulation (to lock down an object like this) is used when there's a notion of coherence that makes sense. If B could be standalone, but is used by both A and C, then there's no reason for it to be "owned" by either A or C, only referenced by them (something else controls its lifecycle). Consider an HTTP server embedded in a more complex program. Where should it "live"?

If at the start of the program you decide that the server should be hidden like this:

  main
    ui
      http
    db
And main handles the mediation between db and ui (or they are aware of each other since they're at the same level), but later on you end up with something like this:

  main
    ui
      http
    db
    admin-ui
And the admin-ui has to push all interactions and receive all its interactions via the ui module, then it may make less sense (hypothetical and off the cuff so not a stellar example, I'll confess, but this is the concept). So you move the http portion up a level (or into yet-another-module so that access is still not totally unconstrained):

  main
    ui
    db
    admin-ui
    http
    -- or --
    web-interface
      http
Where `web-interface` or whatever it gets called offers a sufficiently constrained interface to make sense for your application. This movement happens in applications as they evolve over time, an insistence that once-written encapsulation is permanent is foolish. Examine the system, determine the nature of the interaction and relationship between components, and move them as necessary.

Arbitrary encapsulation is incoherent, I've seen plenty of programs that suffer from that. But that doesn't mean that encapsulation itself is an issue (something to be negotiated, but not a problem on its own).

Re: In defense of complicated programming languages

#260

> The complexity of Rust's ownership model is not added to an otherwise simple program, it is merely the compiler being extremely pedantic about your code obeying rules it had to obey anyway. Not exactly. I remember when I learned Rust a few years ago (so maybe things are different today), sometimes the compiler didn't let me do things that should have been perfectly fine things to do. This forced me to write the cod…

> the compiler doesn't let me have two mutable references to that struct even if one of them only changes X and the other one Y. The semantics of mutable references and other interface constructs must be independent from actual implementation, to allow for changes in the latter. So Rust is behaving as expected here. If the changes to X and Y are truly independent, you can have a function that returns separate mutable…

> then the changes are no longer truly independent.

But the author of the code is telling you that they are independent. The author may be proved wrong if some future change accidentally triggers dependence but we as humans can have knowledge that the compiler can not have. The compiler sees a possibility of conflict and wants to protect you from yourself. That is a usability issue.

Post reply on HN