Conventions in ruby and the principle of least surprise


#1

Hi guys, and hi Matz. I have a few questions about ruby syntax and
library that so far has had me surprised. I know Matz doesn’t say that
ruby shouldn’t surprise me, but rather not surprise him but still, i
would like to hear the reasoning behind a few weird things:

do…end vs. {}

Say I have this function:

def save_block name, &b
@blocks ||= {}
@blocks[name] = b
end

Now this is fine:

save_block :say_hello do
puts “hello!”
end

but not this:

save_block :say_hello {
puts “hello!”
}

I find that rather annoying but I guess it’s to be able to write things
like

save_block [“name1_long”, “name2_long”, “short”].find {|n| n.size <=
5}.to_sym do
puts “hello!”
end

Personally I would rather have had to use parentheses to bind the block
to find, but I realize that it’s hardly up to me :slight_smile:

Anyways. More serious, in my oppinion, is the fact that Array#insert and
String#insert are destructive without having an exclamation mark. Why is
that? I really do not expect them to change their owner.

hi = “hello world”
=> “hello world”

puts hi
hello world
=> nil

puts hi.insert(6, "surprising ")
hello surprising world
=> nil

puts hi
hello surprising world
=> nil

seriously, wtf? Why not insert and insert! that behave as expected?
Before I realized this I had a hard time tracking down some unexpected
behaviour.


#2

Hi,

In message “Re: Conventions in ruby and the principle of least surprise”
on Tue, 27 Jan 2009 11:11:55 +0900, Einar Boson
removed_email_address@domain.invalid writes:

|do…end vs. {}

|I find that rather annoying but I guess it’s to be able to write things
|like
|
|save_block [“name1_long”, “name2_long”, “short”].find {|n| n.size <=
|5}.to_sym do
| puts “hello!”
|end

Correct.

|Anyways. More serious, in my oppinion, is the fact that Array#insert and
|String#insert are destructive without having an exclamation mark. Why is
|that? I really do not expect them to change their owner.

|seriously, wtf? Why not insert and insert! that behave as expected?

The bang (!) does not mean “destructive” nor lack of it mean non
destructive either. The bang sign means “the bang version is more
dangerous than its non bang counterpart; handle with care”. Since
Ruby has a lot of “destructive” methods, if bang signs follow your
opinion, every Ruby program would be full of bangs, thus ugly.

          matz.

#3

Einar Boson wrote:

Now this is fine:

save_block :say_hello do
puts “hello!”
end

but not this:

save_block :say_hello {
puts “hello!”
}

Although this is fine:

save_block(:say_hello) {
puts “hello!”
}


#4

On Jan 27, 2009, at 02:08 , Damjan R. wrote:

My convention is use {} in oneliners. In all other examples I use
do…end.

I follow (and teach) the weirich method:

http://onestepback.org/index.cgi/Tech/Ruby/BraceVsDoEnd.rdoc

{} for block values I care about (think map), do/end for regular block
sends (think each).

I use {} for blocks that return values (think #map) and do/end for all
other blocks (think #each).


#5

My convention is use {} in oneliners. In all other examples I use
do…end.

by
TheR


#6

Mike G. wrote:

Also, I like one rule better than two rules.

Curiously, Ruby departs with 40 years of Structural Programming
tradition - the
statement groups controlled by if-end, and their ilk, do not introduce
a new
variable scope, while the statement groups inside true blocks do
introduce
scoped variables.

Furtherless, the region inside a block might be someone else’s scope!
Rails’s
render :update do … end does this to us. Principle of Most Surprise
applies.

So making dangerous things like blocks look ugly is a good way to help
us
respect them!


#7

Ryan D. wrote:

On Jan 27, 2009, at 02:08 , Damjan R. wrote:

My convention is use {} in oneliners. In all other examples I use
do…end.

I follow (and teach) the weirich method:

http://onestepback.org/index.cgi/Tech/Ruby/BraceVsDoEnd.rdoc

{} for block values I care about (think map), do/end for regular block
sends (think each).

I use {} for blocks that return values (think #map) and do/end for all
other blocks (think #each).

funcs = []
(1…5).each do |i|
funcs << lambda { i }
end
p funcs.map { |f| f.call } # => [1, 2, 3, 4, 5]

funcs2 = []
for j in 1…5
funcs2 << lambda { j }
end
p funcs2.map { |f| f.call } # => [5, 5, 5, 5, 5]

I prefer to make the new binding scope of a block visually obvious,
therefore I always use {}. do/end tricks me into thinking it belongs to
the same category as for/end or while/end or if/end, but it’s quite
different (above).

Also, I like one rule better than two rules.


#8

On Tue, Jan 27, 2009 at 4:12 PM, Ryan D.
removed_email_address@domain.invalidwrote:

{} for block values I care about (think map), do/end for regular block
sends (think each).

I use {} for blocks that return values (think #map) and do/end for all
other blocks (think #each).

Yes, it’s the "I care about’ that matters, a subtle distinction to the
weirich method, which I wrote about:

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

and Jim agreed in the comments.


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale


#9

Awesome :slight_smile: perhaps these little gems of what certain things mean as a
FAQ could make their way into the web site.

Sent from my iPhone

On 27/01/2009, at 1:47 PM, Yukihiro M. removed_email_address@domain.invalid