Forum: Ruby Block Style

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2009-04-27 00:48
(Received via mailing list)
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 Gray II
8f6f95c4bd64d5f10dfddfdcd03c19d6?d=identicon&s=25 Rick Denatale (rdenatale)
on 2009-04-27 02:33
(Received via mailing list)
On Sun, Apr 26, 2009 at 6:48 PM, James Gray <james@grayproductions.net>
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...

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
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2009-04-27 04:19
(Received via mailing list)
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 Gray II
8666d1ebabcea440585dfe831a4af9f1?d=identicon&s=25 Brian Adkins (Guest)
on 2009-04-27 13:02
(Received via mailing list)
Rick DeNatale <rick.denatale@gmail.com> 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...

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.
8f6f95c4bd64d5f10dfddfdcd03c19d6?d=identicon&s=25 Rick Denatale (rdenatale)
on 2009-04-27 14:47
(Received via mailing list)
On Mon, Apr 27, 2009 at 7:00 AM, Brian Adkins <lojicdotcom@gmail.com>
wrote:
> Rick DeNatale <rick.denatale@gmail.com> writes:
>

>> http://talklikeaduck.denhaven2.com/2007/10/02/ruby...
>
> 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
E1d641bfe4071a5413bac781f06d3fd1?d=identicon&s=25 Sean O'halpin (sean)
on 2009-04-27 18:53
(Received via mailing list)
On Sun, Apr 26, 2009 at 11:48 PM, James Gray <james@grayproductions.net>
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 :)

Regards,
Sean
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2009-04-27 19:07
(Received via mailing list)
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.
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2009-04-27 19:11
(Received via mailing list)
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 ;)

Cheers
Robert
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2009-04-27 22:26
(Received via mailing list)
On 27.04.2009 19:11, Robert Dober 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?  :-)

Kind regards

  robert
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2009-04-28 00:08
(Received via mailing list)
On Apr 27, 2009, at 3:25 PM, Robert Klemme wrote:

> On 27.04.2009 19:11, Robert Dober 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?  :-)

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 Gray II
F53b05cdbdf561cfe141f69b421244f3?d=identicon&s=25 David A. Black (Guest)
on 2009-04-28 00:45
(Received via mailing list)
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 Gray 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 :-)


David
E1d641bfe4071a5413bac781f06d3fd1?d=identicon&s=25 Sean O'halpin (sean)
on 2009-04-28 01:09
(Received via mailing list)
On Mon, Apr 27, 2009 at 6:07 PM, Joel VanderWerf
<vjoel@path.berkeley.edu> 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? ;)
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2009-04-28 01:48
(Received via mailing list)
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? ;)

No, I'm puzzled most of the time :)

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...
E1d641bfe4071a5413bac781f06d3fd1?d=identicon&s=25 Sean O'halpin (sean)
on 2009-04-28 02:11
(Received via mailing list)
On Tue, Apr 28, 2009 at 12:47 AM, Joel VanderWerf
<vjoel@path.berkeley.edu> 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
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2009-04-28 09:15
(Received via mailing list)
2009/4/28 James Gray <james@grayproductions.net>:
>
> 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. :-)

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
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2009-04-28 09:59
(Received via mailing list)
On Tue, Apr 28, 2009 at 12:05 AM, James Gray <james@grayproductions.net>
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 ;)

_, *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 ;)

(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 Gray 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.
This topic is locked and can not be replied to.