Often times when I read about "ideal" ways of programming, I'm curious if it's ever implemented in a production code base built by a team.
My gut thinks the solutions will be a little more boring than our inner magpies will want to admit.
31–40 of 226 posts
Often times when I read about "ideal" ways of programming, I'm curious if it's ever implemented in a production code base built by a team.
My gut thinks the solutions will be a little more boring than our inner magpies will want to admit.
Define true as a lambda taking two lazy values that returns the first, and false as one that returns the second, and you can turn all booleans into lambdas with no increase in code clarity. The straw man in the post - talking about a case-sensitive matcher that selectively called one of two different functions based on a boolean - is indeed trivially converted into calling a single function passed as an argument, but…
Someone else addressed the details of your counter argument, but I'd like to respond to it generally. It seems like every time someone writes an article on how to write better code, there are responses about how it doesn't make sense when taken to some logical extreme, or some special case, as if that invalidates the argument. (FP techniques in particular seem to provoke this.) But code design is like other design di…
Earlier quoted context omitted.
Most of this should be obviated when the ? operator is ready. But until then, there is no primitive for 'work on the type you wrapped in Option, short-circuiting and returning None at the first sign of failure', so it has to be done in the library.
If this is an accepted idiom, people will be using it for years to come. Sometimes only to be cool. With "?" and "try!()", Rust is sort of emulating exceptions in a weird way.
A lesson this strengthens in me again is that sometimes a cool-looking idea turns out to be pretty bad in practice, but you only figure it out after you go ahead with it. It isn't bad to try out things (that's how we learn), but you need to be extra honest with yourself about how the thing really feels when you're first using it, and never ignore that sense this new idea actually doesn't fit well and should be rejected.
This just seems to obscure the logic. Not unlike how polymorphism can make code flow harder to read, though feel more clever. There is a place for it - like when you're trying to express a set of logic that will be guarded by the same condition, but always at the cost of some complexity. A set of conditionals is probably the most obvious way to express branching.
Try it before you knock it. I might have said something similar before getting into Haskell, but now I'm nodding along happily. Dealing in meaningful data types with small composable functions is very pleasant for me now.
That's what I recommend too[0] - with the added caveat that you shouldn't be afraid to "knock it" if it turns out to be honestly bad.
Sometimes the idea turns out bad, sometimes it turns out great - but you'll never know it if you don't try; just be honest with yourself during that trial.
Define true as a lambda taking two lazy values that returns the first, and false as one that returns the second, and you can turn all booleans into lambdas with no increase in code clarity. The straw man in the post - talking about a case-sensitive matcher that selectively called one of two different functions based on a boolean - is indeed trivially converted into calling a single function passed as an argument, but…
Someone else addressed the details of your counter argument, but I'd like to respond to it generally. It seems like every time someone writes an article on how to write better code, there are responses about how it doesn't make sense when taken to some logical extreme, or some special case, as if that invalidates the argument. (FP techniques in particular seem to provoke this.) But code design is like other design di…
I've seen junior devs take this kind of stuff literally and over-apply it, like it's a religious ritual that they get a pious buzz from adhering to. I'd prefer people to think first before regurgitating what they most recently learned.
Here's the deal: if is a flow control primitive. Just like goto and while. If (heh) that primitive isn't high-level enough to handle the problem you are facing, it is incumbent upon you as a programmer to use another, higher level construct. That construct may be pattern matching, it may be polymorphism (or any other form of type-based dynamic dispatch). It may be a function that wraps a complex chain of repeated logic, and is handed lambdas to execute based upon the result. It may, as in the article given here, be a funtion that is handed lambdas which apply or do not apply the transformation described.
The point is, there are many branch constructs, or features that can be used as branch constructs, in most modern programming languages. Use the one that fits your situation. And if that situation isn't a that complex, that construct may be if.
Fizzbuzz using guards is the most clean and modifiable fizzbuzz that I've seen in Haskell.
Although now that I think about it, if you provide a function with a list of numbers...
Earlier quoted context omitted.
Try it before you knock it. I might have said something similar before getting into Haskell, but now I'm nodding along happily. Dealing in meaningful data types with small composable functions is very pleasant for me now.
> Try it before you knock it. That's what I recommend too[0] - with the added caveat that you shouldn't be afraid to "knock it" if it turns out to be honestly bad. Sometimes the idea turns out bad, sometimes it turns out great - but you'll never know it if you don't try; just be honest with yourself during that trial. [0] - https://news.ycombinator.com/item?id=12108138
I recommend Bob Harper's essay on "boolean blindness": https://existentialtype.wordpress.com/2011/03/15/boolean-bli... An excerpt: > The problem is computing the bit in the first place. Having done so, you have blinded yourself by reducing the information you have at hand to a bit, and then trying to recover that information later by remembering the provenance of that bit.
Thats why you use Lua, it lets you have multiple return values. So you can get a boolean back to let you know if the strings were the same, an int to know where they ceased matching and a boolean to let you know if they are case different. It's then up to the programmer to decide how much enlightenment they want. The destroy all IF reminds me of GOTO considered harmful of the 70's. There are other ways to fix the pro…
Multiple return values feel elegant also because the compiler can optimize them away when you're not using them, which is the most common case.
Good writing has one clear imperative: communicate meaningfully the intent of the author to the reader. Good code is no different; it is merely expressive writing in a different language, with, perhaps, greater constraint on its intent.
Some people make up rules like "don't use adverbs", or "don't split infinitives", in an effort to write better. But this doesn't necessarily produce good writing; sometimes an adverb is just what you need.
The same is true of code. These are useful things to think about, but "destroy all ifs" is akin to "never use a conjunction".
For instance, at some point there will be a decision made whether the string matching must be case sensitive or not. If the program can do both at runtime, the IF will be, perhaps, in the main (or equiv.).