this post was submitted on 13 Nov 2024
171 points (98.9% liked)

Programmer Humor

32560 readers
361 users here now

Post funny things about programming here! (Or just rant about your favourite programming language.)

Rules:

founded 5 years ago
MODERATORS
 
top 45 comments
sorted by: hot top controversial new old
[–] wasabi@feddit.org 23 points 1 week ago (2 children)

I guess you could call this an OOPsie

[–] yogthos@lemmy.ml 3 points 1 week ago
[–] four@lemmy.zip 22 points 1 week ago (1 children)

I misread Exceptioncatcher as Exceptionhatcher and I think it still fits

[–] xthexder@l.sw0.com 5 points 1 week ago (1 children)

I believe the lifecycle goes ExceptionLayer, ExceptionIncubator, ExceptionHatcher

It's critical you don't throw your exceptions too early, they need to learn to fly first 🀣

[–] Birch@sh.itjust.works 3 points 1 week ago

I thought that's why you throw them?

[–] akariii@lemmy.blahaj.zone 20 points 1 week ago (5 children)
[–] ImplyingImplications@lemmy.ca 27 points 1 week ago (1 children)

Functional Programming Theory: 500 pages of lambda calculus and endofunctors

Functional Programming Practice: Quicksort

[–] yogthos@lemmy.ml 10 points 1 week ago

There's plenty of real world code written in functional languages. Also, this idea that FP is somehow more complex is complete nonsense. Lisp family are some of the easiest languages to learn.

[–] four@lemmy.zip 11 points 1 week ago

Functional programming is perfect for all my projects that I don't start!

[–] yogthos@lemmy.ml 8 points 1 week ago (1 children)

Been working in Clojure for over a decade now, and would never never go back to using imperative/OOP at this point.

[–] balsoft@lemmy.ml 5 points 1 week ago (1 children)

TBH Rust is pretty nice, it borrows (pun intended) a lot of ideas from the functional world (algebraic data types, traits, closures, affine types to an extent, composition over inheritance, and the general vibe of type-driven development), but it's much easier to write fast, efficient code, integrate with decades of libraries in imperative languages, and the ecosystem somehow feels mature already.

[–] yogthos@lemmy.ml 3 points 1 week ago (1 children)

Rust solves a specific problem, and it's good at letting you write correct programs with low resource usage. It's definitely a huge improvement on C and C++.

That said, I find a language like Clojure is far more productive because it's more expressive, and development is done interactively. With Clojure, you start up your program, connect the editor to it, and evaluate code as you go seeing changes live. Once you've worked this way, it's really hard to go back to having to compile your whole program each time you want to see what it's doing. It's like having a conversation with the compiler. It makes it very easy to experiment with different ways to solve a problem, and it gives a lot of confidence because you always see exactly what the code is doing. Clojure also interops with JVM and Js runtimes, so those entire ecosystems are available for use.

Incidentally, there's a Lisp style language that embraces a lot of Rust principles. https://github.com/carp-lang/Carp

[–] balsoft@lemmy.ml 3 points 1 week ago (1 children)

I agree that they fit different niches! My point was that with modern CPU architectures, imperative languages make it much easier to write fast&efficient code just because the hardware was pretty much engineered with C in mind. IMHO Rust offers the best of both worlds when it comes to systems/low-level dev.

[–] yogthos@lemmy.ml 2 points 1 week ago (1 children)

That's not actually true, modern architecture works much closer to the way functional languages work https://queue.acm.org/detail.cfm?id=3212479

[–] balsoft@lemmy.ml 3 points 1 week ago* (last edited 1 week ago) (1 children)

Modern C compilers are a fascinating blend of functional and imperative, that's true; and I didn't say that C is "close to how the modern architectures work". However, mainstream modern architectures are almost always engineered with C in mind primarily, and this is also acknowledged in the article you've linked. Rust, having a lot of similarities to C in terms of its underlying memory model, calling conventions, and control flow primitives, can often benefit from those hardware patterns and optimizations in a way that's more difficult to replicate with a functional language (especially so given most of them are GC-d due to their memory model). The closest I've seen in terms of easy-to-write-quick-code is OCaml, but even there the fast paths are often written in a very much imperative style. Idris2 also seems promising if they manage to get a GC-less mode working. Maybe also Roc, but I've not taken a look at it yet.

Note that I write all of this as someone spending a lot of their work time programming in a functional language (Haskell), with Rust being mostly for hobby stuff. It just always surprises me how much easier it is to write fast code in Rust, and yet also how much of my Haskell intuition was applicable when I was learning it.

[–] yogthos@lemmy.ml 1 points 1 week ago (1 children)

Typically this is true, but it's certainly possible to get comparable performance with functional style. Carp, which I linked above, basically uses the same approach to memory management as Rust. It doesn't rely on GC.

I also find that for most cases it really doesn't matter all that much unless you're in a specific domain like writing drivers, making a game engine, etc. Computers are plenty fast nowadays, and ergonomics tend to be more important than raw performance.

[–] balsoft@lemmy.ml 3 points 1 week ago (1 children)

Typically this is true, but it’s certainly possible to get comparable performance with functional style

It's possible, but you have to specifically write code that's fast, rather than idiomatic or ergonomic, and you have to know what you're doing. At that point, you may have been better off writing it in something else. I feel like OCaml is good at this because it allows you to write abstractions and main control flow in a functional way and hot paths in an imperative way without switching language, but so is Rust.

Carp, which I linked above, basically uses the same approach to memory management as Rust. It doesn’t rely on GC.

I'll take a look, thanks!

I also find that for most cases it really doesn’t matter all that much unless you’re in a specific domain like writing drivers, making a game engine, etc. Computers are plenty fast nowadays, and ergonomics tend to be more important than raw performance.

I mostly agree with you, e.g. Haskell and Clojure, despite being "slow", are plenty fast for what they're used for. On the other hand, I'm very much annoyed when "user-facing" software takes way too long to load or do simple tasks. Java in particular is pretty bad at this: JOSM (Java OpenStreetMap editor) takes longer to load than my entire desktop startup, including a window manager and browser. Unfortunately it's also the best editor around, so I pretty much have to use it to edit OSM, but it still annoys me to no end. Unnecessary computations, IO inefficiencies and layers of wrapping also affect the power consumption quite noticeably.

[–] yogthos@lemmy.ml 2 points 1 week ago

Functional programming tends to get conflated with static typing and immutability, but it's a much broader paradigm than that. I find it really depends on the specific language. It's very easy to do local mutation in Clojure for example. There are constructs like transient specifically designed for doing that. There are also languages like Janet that are functional, but use mutable constructs by default and can embed in C with very low footprint.

I generally get annoyed with software being slow to startup and hogging resources as well, but that's a separate discussion. The JVM is designed primarily for servers where you have long running processes, and startup time isn't really a big concern. So, it's not the best illustration. A CL app written using something like clog will be very snappy.

Joe Armstrong made a good point a while back as well. He pointed out that it's better to focus on making the compiler smarter so that it can do the optimize performance, and optimize languages for human readability. I very much agree with that sentiment. We can see an example of that being done with GraalVM which can compile JVM bytecode into optimized native apps that have very fast startup time and use far less resources than the JVM. At the moment it's not comprehensive, but you can make GUI apps with it, and they'll even run on RPi.

[–] Dirk@lemmy.ml 8 points 1 week ago

You can't spell "functional programming" without "fun".

[–] sentient_loom@sh.itjust.works 1 points 1 week ago (2 children)

Would you make a game with functional programming? Or anything with a GUI?

[–] yogthos@lemmy.ml 3 points 1 week ago

Sure yeah, I've done lots of UI programming with Clojure. It works great. I've also made small games. Why do you think it would be more difficult than with imperative style?

[–] lord_ryvan@ttrpg.network 2 points 1 week ago (1 children)

Lots of things with a GUI, but games are better left for ECS.

Ok. I just read the wiki and it's interesting. What's a good resource for learning deeper about ECS?

[–] Dirk@lemmy.ml 18 points 1 week ago (2 children)

Repeat after me: public static void main(String[] args)

[–] arrakark@10291998.xyz 10 points 1 week ago* (last edited 1 week ago)

blasphemy!

void main(int argc, char ** argv, char ** envp)
[–] quant@leminal.space 6 points 1 week ago (2 children)
public static void main(String[] args)
public static void main(String[] args)
public static void main(String[] args)

Am I summoning the flying spaghetti monster?

[–] dessalines@lemmy.ml 7 points 1 week ago

The object man appears behind you while you're coding and inserts a null reference that takes you a day to find.

[–] Dirk@lemmy.ml 4 points 1 week ago

I'm sorry, but the only spaghetti you get is a 17 levels deep if clause.

[–] TimeSquirrel@kbin.melroy.org 9 points 1 week ago (4 children)

I'm just a hobbyist but...are you guys using exceptions like they're conditional statements?? I thought those were for only when shit is seriously wrong and execution can't continue in the current state. Like if some resource was in a bad state or some input was malformed.

Or maybe I haven't worked on anything complex enough, I dunno.

[–] sbv@sh.itjust.works 13 points 1 week ago

I thought those were for only when shit is seriously wrong and execution can't continue in the current state.

That's how it starts. Nice and simple. Everyone understands.

Until

some resource was in a bad state

and you decide you want to recover from that situation, but you don't want to refactor all your code.

Suddenly, catching exceptions and rerunning seems like a good idea. With that normalized, you wonder what else you can recover from.

Then you head down the rabbit hole of recovering from different things at different times with different types of exception.

Then it turns into confusing flow control.

The whole Result<ReturnValue,Error> thing from Rust is a nice alternative.

[–] yogthos@lemmy.ml 11 points 1 week ago

As a rule, exceptions should indeed be used for behaviors that are outside normal execution flow. For example, you might throw an exception if a file you're trying to access doesn't exist, or a network call fails, etc.

[–] tunetardis@lemmy.ca 4 points 1 week ago (1 children)

I suppose it depends on the language? For the most part I think you're right. Exceptions are only used (if at all) in situations where a program diverges unexpectedly from its normal flow. But take a language like Python. They're just everywhere. Even your plain old for loop ends on an exception, and that's just business as usual.

Python programmers appear to actively promote the 'easier to ask forgiveness, than permission' style nowadays. This article has a measured take: https://realpython.com/python-lbyl-vs-eafp/

[–] Ephera@lemmy.ml 3 points 1 week ago

You don't want to use exceptions in normal control flow, because they're extremely slow. Every time you throw an exception, it has to collect a stacktrace, which is hundreds, if not thousands, of calculations, compared to a handful of calculations for returning a boolean or an enum variant.

[–] undefined@lemmy.hogru.ch 9 points 1 week ago (1 children)

Feeling attacked with Leggable and Fleable. I’ve been known to write a concern or two in Ruby on Rails but what can I say? I like my code DRY.

[–] xthexder@l.sw0.com 3 points 1 week ago (1 children)

My biggest problem with it is that those aren't verbs. You might have LegCount -> Countable and FleaCount -> Countable though.

[–] undefined@lemmy.hogru.ch 2 points 1 week ago

Yeah, that’s pretty annoying.

[–] arrakark@10291998.xyz 9 points 1 week ago* (last edited 1 week ago) (2 children)

I straight up never got a nice answer from StackOverflow on this. Say you have 5 classes, each requiring access to the data members/functions of the others. What's a nice way to solve this problem? I've thought of only two ~~nice~~ shit methods:

  • Pass pointers/shared-pointers etc to each class, but not through the constructor but a setter function
  • Pass lambdas or std::function everywhere. Yuck! Still doesn't put each object in a valid state in the constructor.
[–] yogthos@lemmy.ml 18 points 1 week ago

The. real question is whether this problem needs to be modelled using classes in the first place. The alternative is to just have a set of composable functions that take a piece of data and return a modified version that can be passed to a different function.

[–] Kayana@ttrpg.network 15 points 1 week ago

Well, what problems are you trying to solve by having the classes all access each other's data members? Why is that necessary?

[–] lord_ryvan@ttrpg.network 6 points 1 week ago (1 children)

Does external logging not happen outside of OOP?

If so, why not?

[–] yogthos@lemmy.ml 3 points 1 week ago (1 children)

It does, hence why it breaks the paradigm.

[–] lord_ryvan@ttrpg.network 1 points 1 week ago (1 children)

The paradigm breaks if anything is logged?

I feel like I'm missing a few steps in the logic.

[–] yogthos@lemmy.ml 1 points 1 week ago

Logging tends to happen as a side effect that's completely outside the whole class hierarchy.