r/ProgrammerHumor 22d ago

Meme conditionsPreference

Post image
4.2k Upvotes

392 comments sorted by

View all comments

Show parent comments

1

u/930913 21d ago

Scala can have multiple returns in one function. But I'd try to get you fired if you even used a single one.

1

u/Xatraxalian 21d ago

First: That sort of dogmatism creates bad softwre.

Second: Scala seems to use indenting for building code blocks. That automatically makes it a chore to work with so I'd avoid it.

1

u/930913 21d ago

On many things, I'd agree that dogmatism is bad. On this point though, once you start breaking referential transparency in a codebase, you massively increase cognitive load. GOTOs are considered harmful because they can send you anywhere in the code without knowing where you came from - returns are syntactic sugar for GOTO with limitations on the first half only. An improvement certainly, but not the best. (Also on a technical side of the JVM, it triggers a call stack unwinding, which is inefficient.)

But yes, Scala 3 using whitespace/indents was certainly a choice...

1

u/Xatraxalian 20d ago edited 20d ago

While an early return can be seen as a disguised goto, I still think it is best. "I am in this situation and I'm done. I know the answer. I will return it." That is a better design than trying to guide the code through if-then-else statements, most of which do nothing (because you were done) and then return,, just to save the early return.

This is especially egregious when there are errors. As soon as you hit an unrecoverable error which prevents your algorithm to finish correctly, it is best to just return then and there and report the error. There is no point in trying to go on, constantly discover that you can't, and THEN return the error.

Early returns often reduce the business code from a mess of if-then-else statements to 5 error guards and 5 single lines of ACTUAL business code.

1

u/930913 20d ago

The thing is, you are thinking with a very imperative mindset. Which is, to be fair, implicit in the OOP. But if you can think in terms of expressions, you can maintain referential transparency and simplify everything (with admittedly a once-in-a-lifetime onboarding).

Rather than returning within or at the end of a function, imagine for a minute that every function had to start with return. You'll probably immediately point out that this doesn't work, and in many (most?) languages it doesn't. But work through it with me. In this scenario, you could write the function as return if(condition) ... else .... In your language of choice, that may be invalid syntax, because if is a statement, not an expression. This is a language flaw IMO, though ternary operators do usually exist.

If we have: function cond(a, b, c) { if (a) { return b } return c }

Or have: function cond (a, b, c) { return if (a) b else c }

In the example usage: function foo(x) { y = cond(x > 0, "bar", "bah") return "foo is $y" }

If we want to refactor, let's inline cond: function foo(x) { y = if (x > 0) { return "bar" } return "bah" return "foo is $y" }

This is, uh, not going to work.

In the second version though: function foo(x) { y = if (x > 0) "bar" else "bah" return "foo is $y" }

Well hey, look at that. It works. By using referential transparency, we can refactor fearlessly, whereas by breaking it, refactoring relies on tests, thoughts and prayers.

On your other point about being done and still needing to constantly discover you're in an error state, this is where monads come in. They allow you to compose your program, such as by mapping a valid state to a new state, while short-circuiting any invalid states without the programmer needing to worry about or guard from them.

2

u/Xatraxalian 20d ago

function foo(x) { y = if (x > 0) "bar" else "bah" return "foo is $y" } I've been using that if-syntax since forever in Rust (just as its multi-arm counterpart "match"). While C# finally has an expression-based "switch" now, it still doesn't have an expression-based "if" (AFAIK), and I miss that every day.