Live data from Hacker News

In defense of complicated programming languages

viralinstruction.com

171–180 of 379 posts

Re: In defense of complicated programming languages

#171

Earlier quoted context omitted.

The proof is on the left, and doesn't require algebra. You can use another picture for the right part, like this https://microexcel.ru/wp-content/uploads/2020/05/kvadrat-sum... (it also uses algebraic notation which can be ignored)

Pythagorus's theorem is literally written using algebra. You can't even write it down without algebra.

algebraic notation != algebra

It can totally be written without "algebra"; it's not the notation that contains the idea, it's only the idea that happens to be often expressed in this notation. I could use any other notation, or even plain English, to express the idea, if I didn't mind the extra verbosity.

For Example:

1. The Berlin Papyrus 6619 (from 2000-1786 BC Egypt) uses prose [1]

2. The Ancient Chinese mathematical text Zhuobi Suanjing uses both prose and a pictorial notation [2]

3. The Baudhāyana Shulbasūtra (from 800-500 BC), a set of mathematical instructions for use in the construction of Vedic fire-altars, uses Sanskrit prose describing geometric constructions using rope [3] [4].

[1] https://en.wikipedia.org/wiki/Berlin_Papyrus_6619#/Connectio...

[2] https://commons.wikimedia.org/wiki/File:Chinese_pythagoras.j...

[3] https://en.wikipedia.org/wiki/Baudhayana_sutras#Pythagorean_...

[4] https://en.wikipedia.org/wiki/Shulba_Sutras#Pythagorean_theo...

Re: In defense of complicated programming languages

#172

I remember first encountering classes. I simply could not understand what they were from reading the documentation. Later, I picked op Bjarne's C++ book, read it, and could not figure out what classes were, either. Finally, I obtained a copy of cfront, which translated C++ to C. I typed in some class code, compiled it, and looked at the emitted C code. There was the extra double-secret hidden 'this' parameter. Ding!…

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 understand this thought process, but in my opinion it's the wrong way to think about software concepts. ... just limits your ideas about what is, in fact, an abstract concept.

There's nothing abstract about language constructs. Learning about a language construct via translation is a perfectly fine way of clarifying its semantics, whereas an "abstract" description can easily be too general and fail to pin down the concept; it can also rely on unstated "rules of the game" for how the abstract construct will operate within the program, that not everyone will understand the same way.

Re: In defense of complicated programming languages

#173

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…

> if you need to see an implementation then you're not really understanding the idea for what it is. I 100% disagree. At the very least, I think you're wrong if your assumption is that such a statement applies in general. That statement certainly doesn't fit me as well as many people I've taught in the past. I got my PhD in (pure) mathematics and I could only understand high level abstractions _after_ I worked throug…

But there's a difference between trying to understand, say, a theorem by applying it in concrete situations and by studying its proof.

Re: In defense of complicated programming languages

#174
post #166

As a non-professional and in essence beginner programmer who likes to learn about programming and writes simple to moderately complicated scripts I have to say I am currently in the mindset of not needing classes. In fact the first part of the article I was like: yeah no need for classes there, a function is way simpler. Now like I say I write fairly simple scripts, I mostly automate manual processes, but even after…

Example from the article is a nice one. Now you want to make 100s of different dogs in your program and you want make them to bark at each other. You want to loop over all the dogs and feed them. You can make infinite loop that will check all the dogs and if one is hungry call Feed and if one is not then not. Learn what is the difference between Object and Class. You can create objects and let them live in your progr…

That's not very convincing. Why can't a "dog" be a perfectly ordinary data structure that you manipulate the usual way?

  while True:
      for dog in dogs:
          if dog["hungry"] == True:
              dog["lastfed"] = time()
              dog["hungry"] = False
Okay, I hear you say, but the fact that a dog is an associative array is an implementation detail. Supposing I want to abstract that - don't I need objects then? No! Just define some functions:

  while True:
      for dog in dogs:
          if is_hungry(dog) == True:
              update_last_fed(dog)
              satiate_dog(dog)
So you end up with an abstract type (dog) and a bunch of functions that operate on that abstract type. Is there any advantage to "gluing" the functions to the data? Well yes, but it's subtle. Imagine you had two types of animal:

  while True:
      for pet in pets:
          if species_of(pet) == "dog":
              if is_dog_hungry(pet) == True:
                  update_last_fed_dogfood(pet)
                  satiate_dog(pet)
          elif species_of(pet) == "canary":
              if is_canary_hungry(pet)
              ...etc etc ad nauseam

With objects, you can make this elegant with pet.satiate() etc. Note however that this problem goes away with multiple dispatch - there's no reason why the language couldn't be designed so that satiate(pet) called different functions contingent on the type of pet. We already do this for math! Adding two floats is mechanically distinct from adding two ints, but it's a rare language that makes the programmer use different functions.

Re: In defense of complicated programming languages

#175
post #157

Earlier quoted context omitted.

Such as? The only other things that come to mind when I think about "OOP" are: * inheritance, which at this point even lots of proponents of OOP have stopped defending * encapsulation, which usually gets thrown out the window at some point anyway in sizeable codebases * design patterns, which usually lead to loads of boilerplate code, and often hide implementation behind abstractions that serve little purpose other t…

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 part of Cs state, and we violate the encapsulation. So, we have 2 options:

    1. C -> A -> B
    2. C  A -> B
Either we make C the god-object for A, or we introduce an abstract object X which holds references to A and C (but not to B, because, encapsulation). Both these implementations are problematic: in 1) A now becomes part of Cs state despite C having no business with A, and in 2) we introduce another entity in the codebase that serves no purpose other than as a mediator. And of course, A needs to be changed to accomodate passing the message through to B.

And now a new requirement comes along, and suddenly B needs to be able to pass a message back to C without a prior call from C. B has no reference to A, X or C (because then these would become part of its state). So now we need a mechanism for B to mutate its own state being observed by A, which then mutates its own state to relay the message up to X, which then passes a message to C.

And we haven't even started to talk about error handling yet.

Very quickly, such code becomes incredibly complex, so what often happens in the wild, is: People simply do this:

    A -> B  C
And at that point, there is no more encapsulation to speak of. B, and by extension B is part of A's state, and B is part of Cs state.

Re: In defense of complicated programming languages

#176

I remember first encountering classes. I simply could not understand what they were from reading the documentation. Later, I picked op Bjarne's C++ book, read it, and could not figure out what classes were, either. Finally, I obtained a copy of cfront, which translated C++ to C. I typed in some class code, compiled it, and looked at the emitted C code. There was the extra double-secret hidden 'this' parameter. Ding!…

> Years later, Java appears. I couldn't figure that out, either. I thought those variables were value types. Finally, I realized that they were reference types

I will resist replying to this.

Re: In defense of complicated programming languages

#177
> 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 code in a more complicated way than what should have been necessary.

I think this often happens beacuse the compiler's rules don't always match reality. Like if I have a struct S that contains members X and Y, 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. In reality there is no problem but compiler thinks there is a problem so it forces me to write more complicated code.

I have had this almost exact problem in a program I was writing. The reason I could not just pass X to one function and Y to the other is that one of those functions I had to call was from a library that could only take an S. The solution was that I had to write a macro that extracted Y from S and call that macro every time I wanted to pass Y to a function.

Re: In defense of complicated programming languages

#178

Earlier quoted context omitted.

FWIW, thanks a whole lot! As an engineer in the forties it is somewhat encouraging to read that you felt the same way even if it was about different topics. For me, these days it is about frontend generally, Gradle on backend and devops. So much to learn, so little documentation that makes sense for me. (I'm considered unusually useful in all projects I touch it seems but for me it is an uphill struggle each week.) I…

Groovy? Gradle is a build tool for JVM.

Gradle.

Thankfully Groovy is not in the equation except for old Gradle files from before the Kotlin Gradle syntax existed.

Re: In defense of complicated programming languages

#179
post #150

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 agree that everyone shouldn't need to know every implementation detail, but I'd argue there should be more emphasis on the low-level details in CS education. Programming is often approached from a purely abstract point of view, almost a branch of theoretical mathematics, but imho in 99% of cases it's better understood as the concrete task of programming a CPU to manipulate memory and hardware. That framing forces y…

This depends heavily on the context. In The Art of Computer Programming, the analysis of algorithms is done in terms of machine code. On the other hand, the proverbial centipede lost its ability to move as soon as it started wondering about how it moves.

Re: In defense of complicated programming languages

#180

> 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 references to both ("splits" the struct). But if your library function takes an S, it might rely on the whole struct and then the changes are no longer truly independent.

Post reply on HN