Block Style


#1

I hate to be the guy to start another { … } vs. do … end thread, but I
have some questions I would love to hear opinions on.

I use to just use the rule of { … } for one-liners and do … end for
the longer stuff. However, I’ve recently switched to trying out { … }
for the times when I care about the return value and do … end for the
times the block is for side effects. For the most part, I do like the
new strategy, but sometimes I have trouble deciding which to use.

Let me give two examples that have made me stop and think.

First, tap() is Ruby 1.9 is a little tricky. I do care about the
return value, but not the return value of the block, so which strategy
should I use? It seems like do … end is more correct, but that seems
a lot uglier in practice:

arr.sort.tap do |sorted|
p sorted
end.whatever…

Another example is with a transaction() method for a database. When
using such a tool, I often end up with calls where I care about both
the side effects (transactional behavior) and the return value:

db.transaction {
db[:count] += 1
db[:count]
}

Any thoughts on how edge cases like this mesh with the block strategy?

James Edward G. II


#2

On Sun, Apr 26, 2009 at 6:48 PM, James G. removed_email_address@domain.invalid
wrote:

I hate to be the guy to start another { … } vs. do … end thread, but I have
some questions I would love to hear opinions on.

I use to just use the rule of { … } for one-liners and do … end for the
longer stuff. However, I’ve recently switched to trying out { … } for the
times when I care about the return value and do … end for the times the
block is for side effects. For the most part, I do like the new strategy,
but sometimes I have trouble deciding which to use.

Well, I went back to what I wrote over a year ago on this
“controversy” and I think it stands up pretty well.

http://talklikeaduck.denhaven2.com/2007/10/02/ruby-blocks-do-or-brace

I for one, eschew dogmatism. When I went back and re-read that article
I was pleased to see that I was far from dogmatic. I talk about
leaning or tilting one way or the other. This is more about
aesthetics than laws of nature or rocket surgery.

Here I’d go with braces, because as I say in that article, that

end.whatever

just grates with me. On the other hand if it was

var = arr.sort.tap.do | sorted|
#…
end

I would probably lean more towards do/end, but then again, if the
block was a one-liner like yours then I might well 'break the tie"
with the one-vs-multi line idea

var = arr.sort.tap {|sorted| p sorted}

In fact I’d probably be inclined to write your example on one line

arr.sort.tap {|sorted| p sorted}.whatever

Another example is with a transaction() method for a database. When using
such a tool, I often end up with calls where I care about both the side
effects (transactional behavior) and the return value:

db.transaction {
db[:count] += 1
db[:count]
}

This one looks a little odd to my eye just because most of the db
transaction delimiting methods I’ve seen don’t return the value of the
block, they are concerned simply with marking a transaction boundary.


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale


#3

On Apr 26, 2009, at 7:32 PM, Rick DeNatale wrote:

db[:count]
}

This one looks a little odd to my eye just because most of the db
transaction delimiting methods I’ve seen don’t return the value of the
block, they are concerned simply with marking a transaction boundary.

Interesting. I’ve been using Amalgalite a lot lately. It definitely
returns the value. PStore in the standard library does as well. I
have double-checked ActiveRecord, but my expection would be that it
does too. Maybe I’m wrong though.

James Edward G. II


#4

On Mon, Apr 27, 2009 at 7:00 AM, Brian A. removed_email_address@domain.invalid
wrote:

Rick DeNatale removed_email_address@domain.invalid writes:

http://talklikeaduck.denhaven2.com/2007/10/02/ruby-blocks-do-or-brace

FYI - looks like you have an extra trailing paren here in the last
code example:

description ‘pickup up the leaves’
task(:rake => pre_raking_tasks) {#code to rake the leaves})

Yeah that was a cut and past typo in the sequence of examples.

BTW - this is one of the reasons I tend to use parens on function
invocations consistently.

I also tend to use more parentheses than most rubyists, but I don’t
normally use them in the face of a DSL method, so I use

attr_reader :foo
alias_method :meth1, :meth2
has_many :widgets, :through => :gadet_widget_usage
task :doit => :didit

rather than

attr_reader(:foo)
alias_method(:meth1, :meth2)
has_many(:widgets, :through => :gadet_widget_usage)
task(:doit => :didit)

As I said eariler, I eschew dogmatism. I agree with Ralph Waldo
Emerson’s “A foolish consistency is the hobgoblin of little minds.”


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale


#5

Rick DeNatale removed_email_address@domain.invalid writes:

Well, I went back to what I wrote over a year ago on this
“controversy” and I think it stands up pretty well.

http://talklikeaduck.denhaven2.com/2007/10/02/ruby-blocks-do-or-brace

FYI - looks like you have an extra trailing paren here in the last
code example:

description ‘pickup up the leaves’
task(:rake => pre_raking_tasks) {#code to rake the leaves})

BTW - this is one of the reasons I tend to use parens on function
invocations consistently.


#6

On Sun, Apr 26, 2009 at 11:48 PM, James G. removed_email_address@domain.invalid
wrote:

such a tool, I often end up with calls where I care about both the side

I prefer {} when using blocks in a functional style, do…end when
using statements but I’m with Rick on the ‘foolish consistency’.

In your first example, it would appear that you ~are~ concerned with
the return value of the block as you call #whatever on it, so I’d use:

arr.sort.tap { |sorted|
p sorted
}.whatever…

assuming that the ‘p sorted’ stands for a multiline statement. In 1.9
you can even do:

arr
.sort
.tap { |sorted| p sorted }
.whatever…

if you’re so inclined.

In the second you’re not using the return value, so

db.transaction do
db[:count] += 1
db[:count]
end

would seem appropriate.

I’m using this in DSL-type code:

person = Person :name => “Arthur” do
age 42
end

and find it more aesthetically pleasing than:

person = Person(:name => “Arthur”) {
age 42
}

Just my tuppenceworth :slight_smile:

Regards,
Sean


#7

Sean O’Halpin wrote:

In your first example, it would appear that you ~are~ concerned with
the return value of the block as you call #whatever on it, so I’d use:

arr.sort.tap { |sorted|
p sorted
}.whatever…

Actually, the return value of #tap is different from the return value of
the block (which is ignored by tap, IIRC). That puzzled me for a moment.


#8

I have adapted a very simple rule for a long time now, if I use the
block for the side effect it is do…end, always.
If I use it for its return value it is { } always, (BTW thank you Rick
;).
But of course when I do side effects and use the return value I am
lost. (I guess that it is {} for one liners and do end otherwise than)

I know this was very helpful :wink:

Cheers
Robert


#9

On 27.04.2009 19:11, Robert D. wrote:

I have adapted a very simple rule for a long time now, if I use the
block for the side effect it is do…end, always.
If I use it for its return value it is { } always, (BTW thank you Rick ;).
But of course when I do side effects and use the return value I am
lost.

I don’t have the problem, because I always do this:

(I guess that it is {} for one liners and do end otherwise than)

One liner and multi liner is actually exclusive, which cannot be said of
return value and side effects. Maybe my sticking to the syntactical
convention is the reason why I never bother to think about which rules
to use. James, maybe that can ease your “pain” as well? :slight_smile:

Kind regards

robert


#10

Hi –

Sean O’Halpin wrote:

Let me give two examples that have made me stop and think.
Another example is with a transaction() method for a database. When using
James Edward G. II
arr.sort.tap { |sorted|

if you’re so inclined.

Though not in irb. (And, preferably, not anywhere, but that’s probably a
lost cause :slight_smile:

David


#11

On Apr 27, 2009, at 3:25 PM, Robert K. wrote:

On 27.04.2009 19:11, Robert D. wrote:

(I guess that it is {} for one liners and do end otherwise than)

One liner and multi liner is actually exclusive, which cannot be
said of return value and side effects. Maybe my sticking to the
syntactical convention is the reason why I never bother to think
about which rules to use. James, maybe that can ease your “pain” as
well? :slight_smile:

It’s definitely easier, but it can lead to less pretty code too, like:

whatevery.map do |e|
# multiple lines of calculation here…
end.find { … }

That’s one reason why I was looking into the other option.

James Edward G. II


#12

Sean O’Halpin wrote:

block (which is ignored by tap, IIRC). That puzzled me for a moment.


vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

You’re right - sloppy wording on my part. But you were only puzzled
for a moment, right? :wink:

No, I’m puzzled most of the time :slight_smile:

Does it make a difference, in choosing between

foo {…}.bar

and

foo do…end.bar

whether the value is the block value or the #foo return value (in those
cases where they differ)? Probably not, just a thought…


#13

On Tue, Apr 28, 2009 at 12:47 AM, Joel VanderWerf
removed_email_address@domain.invalid wrote:

I guess it’s in the eye of the beholder but

end.bar

just looks odd to me.

And now I think about it, when we do

arr.map {|x| x * x }.sort

we’re not operating on the return value of the block either but

arr.map do |x| x * x end.sort

just looks wrong to me.

Regards,
Sean


#14

On Mon, Apr 27, 2009 at 6:07 PM, Joel VanderWerf
removed_email_address@domain.invalid wrote:

block (which is ignored by tap, IIRC). That puzzled me for a moment.


vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

You’re right - sloppy wording on my part. But you were only puzzled
for a moment, right? :wink:


#15

2009/4/28 James G. removed_email_address@domain.invalid:

It’s definitely easier, but it can lead to less pretty code too, like:

whatevery.map do |e|

multiple lines of calculation here…

end.find { … }

That’s one reason why I was looking into the other option.

Well, I personally do not find that “less pretty” - but this lies in
the eye of the beholder. :slight_smile:

If it comes to prettiness of code, for me these items are more
important:

  • proper indentation,
  • avoidance of deep nesting of control structures,
  • wise use of empty lines to visually separate logical bits of code.

But I’m digressing…

Kind regards

robert


#16

On Tue, Apr 28, 2009 at 12:05 AM, James G. removed_email_address@domain.invalid
wrote:

It’s definitely easier, but it can lead to less pretty code too, like:

…and being easier it just does not convey any additional information.

I adapted my style because I thought - and still think - that
providing the information of side effect is of value to the reader of
the code.
I am aware that being used to the style is also very valuable to the
reader, but that is a long, interesting and complicated matter, which,
if not OT is Super Topic in the mathematical sense of superset.
That said I should probable stick to {} if there is no side effect to
the block (1), thus indicating functional style and to do end for
anything else, because if the return value of the block is used or not
should actually be well documented.
Which is worth yet another discussion.

That would, e.g give, to cite myself :wink:

_, *args = whatever.split.sort_by{ … }
inject{ } # while inject do … end should raise an alarm bell

and

each.do |x| puts x end # sorry if this example is too sophisticated :wink:

(1) Updating closed in locals considering a side effect or not?

Cheers
Robert

whatevery.map do |e|

multiple lines of calculation here…

end.find { … }

That’s one reason why I was looking into the other option.

James Edward G. II


Si tu veux construire un bateau …
Ne rassemble pas des hommes pour aller chercher du bois, préparer des
outils, répartir les tâches, alléger le travail… mais enseigne aux
gens la nostalgie de l’infini de la mer.

If you want to build a ship, don’t herd people together to collect
wood and don’t assign them tasks and work, but rather teach them to
long for the endless immensity of the sea.