Live data from Hacker News

Please, kill your darlings

blog.ikura.co

51–60 of 84 posts

Re: Please, kill your darlings

#51

Great post. This is one of my biggest pet peeves with Ruby and languages like it; they encourage developers to "show off" by using the most esoteric features they can find (even better if these features use weird symbols). Compared to a language like Python that has few neuroses, the same developer writes much less readable code. The post may have been better if the author included a Python sample that does the same…

Something like the following: capitalize = lambda word: "{}{}".format(word[0].upper(), word[1:]) new_string = ' '.join(map(capitalize, long_string.split(' '))) or: new_string = ' '.join(["{}{}".format(word[0].upper(), word[1:]) for word in long_string.split(' ')]) is much clearer and simpler to me, because it explains the intention of the code: split the string into words, map each word to it's capitalized version, t…

For me, readability is about how intuitive something is, how much can be understood with basic knowledge of programming in general and perhaps a quick primer on the specific language. I think this is the best measurement because it emphasizes a reliance on the most commonly used, general concepts, and encourages people to use those unless there's a good reason not to. A side benefit is that the more basic language primitives and flow controls tend to be more performant, better tested, and have fewer weird edge cases where they behave in an unexpected manner.

I dislike your first example because

a) imo, the lack of spacing makes it harder to see what's going on. it's more obvious that something is being iterated in a for loop with a new indentation level than in the map function.

b) it depends on the Python-specific implementation of lambda. The behavior of a lambda varies substantially from language to language and lambdas are used rarely enough that it's pretty likely someone who doesn't spend all day every day in Python is going to have to go back and look up the specific behavior. The syntax is much less clear than a full function definition.

In my opinion, it's much easier to read code like:

    def capitalize(str):
        return "{}{}".format(str[0].upper(), str[1:])

    for word in long_string.split(' '):
        captizalize(word)
    ...
This makes it much more obvious what's going on, and it should be readable to anyone with a passing knowledge of Python, and possibly anyone with a knowledge of programming languages in general. Invocation of map and lambda in this case only make the intent of the program more obscure.

I'm not saying that map or lambda are never appropriate to use; sometimes they are. But I don't think it's wise to use them when more basic, universal language constructs do an equally adequate job, especially if the only benefit is "fewer lines of code".

The second example is just a list comprehension form of my original example, which IMO is less readable for much the same reasons. If you're not super familiar, you'll need to go back and look up list comprehensions. There is no spacing to make it obvious that something particular is being iterated or branched.

I understand that ultimately, ease of reading comes down to what style one is most familiar with, which makes it subjective, as you said. But I think there is a stronger rational basis for always preferring the simplest construct that adequately performs the function, which is that in the general case, there is less need to refer back to docs, less possibility of unexpected behavior, and less possibility of strange performance issues.

Re: Please, kill your darlings

#52
post #33

Earlier quoted context omitted.

Yes, that is almost exactly how I would rewrite it. The original doesn't communicate its intent at all. It makes one itching to rewrite it but, it takes some time to take all the "smartness" into account. The original line raises surprisingly many questions: - The default for `split` is to split on whitespace. Is it the intent of the author to only split on spaces? (I guess so) What about tabs? - Why is the author us…

One-liners are fine if you know you'll never need to extend the logic later on. Problem is, how often does that stay true? For instance your 'titleize' method, in order to properly title case any string, ought to support a second option of words to ignore, such as "a, an, the, ...". If you wrote it as a one-liner at first, then you've got to go into your mapper and add conditionals if an ignore list is passed, and yo…

> One-liners are fine if you know you'll never need to extend the logic later on. Problem is, how often does that stay true?

Write the simple version first, wait for the need to extend the logic arise naturally, factor out the function and extend.

Re: Please, kill your darlings

#53
post #11

Earlier quoted context omitted.

I agree. Someone's "darling" code might be a truly innovative or unique way of solving a problem, and providing enough commentary should help avoid the problems the author warns about.

It's amazing, their whole complaint is solved by proper software development practices - Comment your code.

Code that's clear enough not to need a comment is better than code that has a comment. Comments can be outdated or mistaken and they consume screen real estate.

Re: Please, kill your darlings

#54

Either comment your code or move darlings to a properly named function, which is even better. @sentence = capitalizeEveryWordInThe(@sentence) Then you can both unit test (if you're into that kind of thing) and refactor without breaking a lot of things.

I've found I often prefer reading and writing the actual guts of the inlined function, over naming it something semantically significant. It's often difficult to craft a good English name for the function. It can inhibit understanding to have to change context to learn what a function does by looking at its code (OtherModule.capitalizeEveryWordInThe). There can be small semantic variations (capitalizeEveryWordInThe, capitalizeEveryWordButFirstInThe) requiring an exploding util package.

Edit. I'm saying I find the one-liner form frequently more understandable than one embodied in a function.

Re: Please, kill your darlings

#55
post #3

Maybe I'm still just showing off, but I don't even use Ruby that often and it's pretty clear what that code does. Split the sentence into words, capitalize the first letter of each word, and re-join the words into a single string.

Same. Don't know Ruby at all, but in this case the language is pretty straightforward. Perhaps Ruby was the wrong example. Let's get more magical with Perl:

  map s {(\w+)} {\u$1}g, @sentence;
I'll give a dollar to anyone who already knows Perl and can tell me what the fuck is going on there.

Re: Please, kill your darlings

#56
post #11

Earlier quoted context omitted.

It's amazing, their whole complaint is solved by proper software development practices - Comment your code.

Code that's clear enough not to need a comment is better than code that has a comment. Comments can be outdated or mistaken and they consume screen real estate.

You still need to document intent. No code is self documenting.

Re: Please, kill your darlings

#57
post #14

I'm curious to see how more experienced Rubyists on HN would write this. My stab: @sentence.split(' ').map(&:capitalize).join(' ') More terse and more descriptive (imo)

Man, my pet hate with Ruby is all the weird extra punctuation characters.

Here's the Perl version:

    $_ = join ' ', map ucfirst, split / /;

Re: Please, kill your darlings

#58
There are examples of code which is unnecessarily complex, or difficult to read, but this is not a very good example. I'll join the chorus of those who say they don't even know Ruby and yet they could easily figure out what the example was doing.

The most you could could say about this example, IMHO, is that it could be made more readable by breaking it up into more than one line.

The last thing we need is more people writing C code in dynamic languages that have much better and more concise programming constructs. If a programmer does not wish to learn how to use these constructs, that's their business. But I intend to use all the power available to me in whatever language I am programming in -- when it is appropriate, of course.

Just because some programmers don't understand how a particular language construct works, that does not make its use the product of a self-absorbed hacker who thinks he has "superpowers".

Yes, we should all guard against writing code that is difficult to read, but I think such code is rarely caused by the language features we use (although some languages make that easier than others -- I'm looking at you, C++). In fact, you could argue that it is more commonly caused by the features we don't use. Unnecessary verbosity is often the product of someone who is new to a particular programming language, and I think most of us are happy to discover shorter, simpler, clearer ways of expressing things.

In fairness to the author of the article, I think there is a point when you can chain a few too many things together without any intertwined temporary variables, separate function calls, or at least comments, and the reader of your code can get lost. However, in my mind, the code example given hasn't quite reached that threshold yet.

Re: Please, kill your darlings

#59
Yesterday, I was complaining that Rust encourages this sort of thing. In Rust, it comes from the error handling, though. You have to write lots of

    x().and_then(|foo| exp).and_then(|foo| exp)
to handle errors. The imperative form requires a match statement after every function call that can return an error. Or "try!()", which bails with a return. It looks like we just have to get used to this Haskell-like style.

Re: Please, kill your darlings

#60
post #3

Maybe I'm still just showing off, but I don't even use Ruby that often and it's pretty clear what that code does. Split the sentence into words, capitalize the first letter of each word, and re-join the words into a single string.

Same. Don't know Ruby at all, but in this case the language is pretty straightforward. Perhaps Ruby was the wrong example. Let's get more magical with Perl: map s {(\w+)} {\u$1}g, @sentence; I'll give a dollar to anyone who already knows Perl and can tell me what the fuck is going on there.

In rough English, it uppercases the first letter every word in every element of the sentence array.
Post reply on HN