As a java guy and think python is weird, I don't think this sucks.
But, I also agree that can serve as terrible intro to programming if you start programming right away without understanding the basics of abstractions. But, often when we have tools either designed for a purpose in mind or a dominant paridigm or reaction to existing set of tooling, this can result in understandable yet extreme abstractions.
Java is designed with OOP in mind and it kind of makes sense to have the user to think in terms of lego blocks of interfaces. Every method or class needs to have clear understanding of its users.
public - software handle is for all users
protected - software handle for current and extending classes
default - software is exposed to current package
private - software is restricted to be used in current class alone and nowhere else
So, the beginning of java programming starts with interface exposed to the user or other programmers. Is it weird and extreme. Yes. At least, it is consistent.
This heavyweight syntax has nothing do with OOP. No common definition of OOP (before Java came into existence) said that functions cannot exist outside of a class.
Java choose to adopt object oriented purity in a very weird way. Sun has initially rejected the idea of standalone functions (static import was introduced in Java 5, closures in Java 8 and compact source files/instance main is only coming now). Even with static imports and compact source files, there is still a class holding these methods behind the scenes (for practical and compatibility reasons, more than to achieve any ideological or theoretical goals at this point). That seems like Sun was trying to keep the purity of "everything is an object", but at the same time they've introduced primitive values, which are clearly not objects. This was a pretty bad decision in my opinion. There is no practical benefit from requiring every function to be defined inside a class[1]. On the other hand, being able to have methods defined on an integer is a pretty nice feature.
If we look at a Smalltalk, which is generally considered be an example of a "pure" OOP language, we see a striking difference. This is Hello World in Smalltalk:
'Hello, world!' printNl
Smalltalk allows you to freely define functions outside of classes and even write code that is not part of a class (directly in the REPL).
I am not sure of historical significance of what OOP is, but even Alan Kay seem to agree that modern definition of OOP is not what he intended[1]. But, for better or worse we are stuck with the principles.
We even have design patterns like Command, to workaround first class functions in "pure" OOPy way.
And for enterprise software development, I like it that way. It can make up a definition it wants and stick to it. I think it is better for a language's ecosystem and culture to have one dominant paradigm than becoming kitchen sink of programming languages.
I agree, this is the whole point of "Hello world", which is to show the boilerplate required to start a program capable of outputting text, as well as the actual code that outputs that text. It's also the chance to get the build tools setup, people forget that some of the 'boilerplate' is the actions required to build, which often are much more involved for newer tools and frameworks.
You can just say initially e.g. when I learned C++ that "#include <iostream>" can be "mostly ignored for now, but just know iostream stands for Input / Output stream, and this line is necessary to e.g. output with std::cout"; and there are no scars left from this.
Some scars... as cout << was always a bad idea, and taught people early on in their C++ development that being overly clever with operator overloading was expected
Which I think most people understood not to do themselves when they encountered their first issue with the precedence of << vs other operators in expressions they wanted to print.
Another thing that impedes us sunken cost fallacy. Classic "Simple vs easy" change. Even if a design is comparatively simpler, it is harder to make such change for small feature.
We had a project which is supposed to convert live objects back into code with autogenerated methods. The initial design was using a single pass over the object graph and creating abstractions of HDL and combining method blocks in the same pass.
That is a big hairy code with lot of issues. Simpler would be to handle one problem at a time - method generation in one pass and then convert the methods to HDL. But, getting approval for a deployed app is so hard. Particularly when it is a completer rewrite.
I'm not sure what I'm supposed to see there. From my point of view, this is a low-effort, vibe coded app that doesn't solve the problem the OP had but it's solving a different one. You'd need to at least train a small classifier based on something like BERT to actually address the issue.
What I showed in my comment just demonstrates that this doesn't solve the problem OP had.
Huge respect for all your articles and work on llms, but this example should have been using AI to create a tool that uses AI to intelligently filter hacker news :)
I think I have mentioned this before in HN too. I am not from CS background and just learnt the trade as I was doing the job, I mean even the normal stuff.
We have a project that tries reify live objects into human readable form. Final representation is so complicated with lot of types and the initial representation is less complicated.
In order to make it readable, if there is any common or similar data nodes, we have to compare and try to combine them i.e. find places that can be made into methods and find the relevant arguments for all the calls (kind of).
Initial implementation did the transformation into the final form first, and then started the comparison. So, the comparison have to deal with all the different combinations of the types we have in final representation now, which made the whole thing so complex and has been maintained by generation of engineers that nobody had clear idea how it was working.
Then, I read about hashmap implementation later (yep, I am that dumb) and it was a revelation. So, we did following things:
1. We created a hash for skeleton that has to remain the same through all the set of comparisons and transformation of the "common nodes", (it can be considered as something similar to methods or arguments) and doing the comparison for nodes with matching skeletal hashes and
2. created a separate layer that does the comparison and creating common nodes on initial primitive form and then doing the transformation as the second layer (so you don't have to deal with all types in final representation) and
3. Don't type. Yes. Data is simplest abstraction and if your logic can made into data or some properties, please do yourself a favor and make them so. We found lot of places, where weird class hierarchies can be converted into data properties.
Basically, it is a dumb multi pass decompiler.
That did not just speed up the process, but resulted in much more readable and understandable abstractions and code. I do not know, if this is widely useful but it helped in one project. There is no silver bullet, but types were actual problem for us and so we solved it this way.
reply