I have accidentally discovered the Ruby idiom of ||=()
,
as in:
def app_logger
@app_logger ||= (
logfile = File.open(::Rails.root.join(LOG_FILE), 'a')
logfile.sync = true
AppLogger.new(logfile)
)
end
I tried to use {}
instead of ()
, but it didn't work. I thought {}
is meant to enclose a block.
Is this a known idiom? Is it a good style?
I haven't found much documentation on this kind of use of parentheses. Any pointers would be helpful.
Please note this post is about the use of ()
this way, not the use of ||=
. There are many posts about this latter idiom already.
Like a lot of things in Ruby that can be done, many of them shouldn't be done and this is one of them.
Using brackets to group code when there are already other facilities is potentially confusing and almost certainly contrary to many coding style guides. If I saw this in code I was managing, I'd immediately fix it.
What's best is to use the begin
/end
markers to make it completely clear what's going on:
def app_logger
@app_logger ||= begin
logfile = File.open(::Rails.root.join(LOG_FILE), 'a')
logfile.sync = true
AppLogger.new(logfile)
end
end
A lot of things in Ruby evaluate down to a single value, and the contents of (...)
are apparently one of those things.
Your other example of foo ||= (a=10; a+10)
being "nicer" is also rather controversial. This does two assignment and addition, but only conditionally. This one would almost always be better written in long-form with begin
/end
to make it clear that a+10
is the result.
From a style perspective, hiding the "important" part of that, the a+10
, at the end of a line is bad, it can be overlooked. Having it as the last line makes it abundantly clear. This is also why having if
statements tacked on the end of long lines is also bad, it hides that the line is only conditionally executed.
Concern for brevity is always trumped by concerns of readability. Saving a couple of bytes on disk is not going to help you one bit when, due to someone misreading your code, they introduce a crippling bug.
That someone could be you in the future when you get caught up in your own cleverness. It's happened to all of us at some point.
In addition to @tadman's answer, I'd counter that writing the method in a more Ruby-like way maintains the readability and keeps it nice and tight:
def app_logger
return @app_logger if @app_logger
logfile = File.open(::Rails.root.join(LOG_FILE), 'a')
logfile.sync = true
@app_logger = AppLogger.new(logfile)
end
For my intent and rationale in writing it this way, see my comment to this answer in response to @fotanus's comment.
Our brains become conditioned to see patterns as we learn to program and learn new languages. Whether the patterns are in hexcode emitted by a debugger or C, Perl, Python, Java or Ruby, we still see patterns. When we're used to seeing them we tend to want to change code to resemble those familiar constructs. That's why Perl and C programmers tend to write Java, Python and Ruby like C and Perl at first. The "beauty is in the eye of the beholder", but that same beauty is a weed when it's out of place, just as wild-flowers are out of place in formal gardens or the middle of a fairway. We should write in the vernacular of the programming language, because that is the way of speaking expected by those who live in that land.
Something to remember is, though we, personally, might be a code-studly monster who can reduce code from multiple lines down to a single character, what will keep me in a job is the ability to write code other people can understand, without necessarily having to be of the same caliber. Code crunching invariably reaches a point of diminishing returns, well before it has been reduced to its minimal size, especially when that code is running in a production environment and is being maintained by junior programmers and there is a need to extend or debug it, and the clock is ticking. Minutes = dollars
where I work, and the dollars have really big multipliers, which causes the walls to leak managers like cockroaches when a bug-bomb goes off. (Oh... did I call managers cockroaches? Whatever.)
Being called the morning after an event and being thanked for writing code that made it easy for them to debug/fix or amend/extend is a whole lot better than being called at 2:45AM and asked to get online to help. I value my sleep and that of my coding-partners and push for maintainable code as our #1 priority.
That's my $0.02.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With