Procs and blocks


(Evan Martin) #1

When a need to use a callback function, it seem there are two ways to go
about doing it:

  1. Take a block:
    def set_cb_1(&p)
    $cb = p
    end

(or, similarly,)

def set_cb_1
$cb = Proc.new # Proc.new by default wraps the block
end

  1. Take a Proc:
    def set_cb_2§
    $cb = p
    end

To use these functions, then:

set_cb_1 { … }
set_cb_1 do … end

or

set_cb_2 proc { … }
set_cb_2 Proc.new { … }

and if I make a function
def somefunc

end
I can use it like:
seb_cb_1 { somefunc() }
set_cb_2 method(“somefunc”) # weird

Are they equivalent? Are the two forms of set_cb_1 I wrote above the
same thing? It seems the first one sets $cb to a block, while the
latter sets $cb to a proc that wraps a block. Or are a proc and a block
the same thing?

Is either way considered preferable? I’m wrapping a C library that uses
callbacks, and I found myself writing this code, where I explicitly
support both (ignore that “type” variable, it’s unrelated to this):

if (rb_block_given_p()) {
    rb_scan_args(argc, argv, "1&", &type, &cb);
    /* behaves like set_cb_1; cb is the provided block */
} else {
    rb_scan_args(argc, argv, "2", &type, &cb);
    /* behaves like set_cb_2; cb is the explicitly passed proc */
}

It seems they should almost be the same thing?

···


Evan Martin
martine@cs.washington.edu
http://neugierig.org


(Kyle Rawlins) #2

Someone can probably give you a more thorough answer, but my understanding of
it is that they are not the same thing. However, for your purposes (when you
are storing a callback) you can really only use a proc.

The way I understand it is that a block is sort of a iconoclastic element of
ruby in that it really isn’t an object, and is only sort of an argument to
whatever function it is given to. This is for efficiency reasons, which makes
sense considering that the main method of iterating over anything in ruby is to
use a block. So it is much faster to use a combination of a block with yield
for your basic iterators. However, I don’t think you can store a block by
itself, you have to wrap a proc object around it. I believe that all proc
objects (created the normal way) store blocks. So a block is just a proc
that can be used directly without the overhead of creation, calling, etc.

Also, for two of your solutions, the main difference for your purposes as far
as I can see is that you get different failure modes: (code not reproduced
exactly, sorry)

given
def a(&p)
$cb = p
end

def b
$cb = Proc.new
end

irb(main):009:0> a # nil is silently stored to $cb:
nil
irb(main):032:0> $cb
nil
irb(main):033:0> b
ArgumentError: tried to create Proc object without a block
from (irb):29:in new' from (irb):29:inb’
from (irb):33

It seems that for a formal proc parameter there is a default value of nil,
resulting in a reference to nil rather than the ArgumentError one might expect
when it isn’t given.

Thinking about it, you might simply be better off for the purpose of callbacks
taking the callback function as a normal argument; this way you can not specify
a default of nil if you don’t want that, or you could give your own default.

def a§
$cb = p
end

a(lambda do |arg| p arg.inspect end)

hope this helps
-kyle

···

On Thu, Jun 13, 2002 at 02:40:01AM +0900, Evan Martin wrote:

latter sets $cb to a proc that wraps a block. Or are a proc and a block
the same thing?


http://mas.cs.umass.edu/~rawlins

There is no friend
anywhere.


(ts) #3

  def set_cb_1(&p)
    $cb = p
  end

When you write this, this is like if you have written

     def set_cb_1
        p = nil
        if block_given?
           p = Proc.new
        end
        $cb = p
     end

Guy Decoux


(Jean-Hugues ROBERT) #4

Hello,

For what its worth: Handling both Proc & block parameters

def a( block = nil, &syntax_block )
block = syntax_block if syntax_block

or block = syntax_block unless block

block.call()
)

b = Proc.new do print “hello” end
a b # Works with Proc
a do print “world” end # And block too

The main drawback is that optional parameters become mandatory when
one want to use Proc object instead of block:
def err( msg = “Err”, b = nil, &sb )
b = sb if sb
b( msg)
end
a = Proc.new do |x| print x end
err do |x| print x end
err “Hello” do |x| print x end
err “Hello”, a
err( , a) # Not in Ruby syntax…

Yours,

Jean-Hugues

···

At 03:10 13/06/2002 +0900, you wrote:

On Thu, Jun 13, 2002 at 02:40:01AM +0900, Evan Martin wrote:

latter sets $cb to a proc that wraps a block. Or are a proc and a block
the same thing?

Someone can probably give you a more thorough answer, but my understanding of
it is that they are not the same thing. However, for your purposes (when you
are storing a callback) you can really only use a proc.

The way I understand it is that a block is sort of a iconoclastic element of
ruby in that it really isn’t an object, and is only sort of an argument to
whatever function it is given to. This is for efficiency reasons, which makes
sense considering that the main method of iterating over anything in ruby
is to
use a block. So it is much faster to use a combination of a block with yield
for your basic iterators. However, I don’t think you can store a block by
itself, you have to wrap a proc object around it. I believe that all proc
objects (created the normal way) store blocks. So a block is just a proc
that can be used directly without the overhead of creation, calling, etc.

Also, for two of your solutions, the main difference for your purposes as far
as I can see is that you get different failure modes: (code not reproduced
exactly, sorry)

given
def a(&p)
$cb = p
end

def b
$cb = Proc.new
end

irb(main):009:0> a # nil is silently stored to $cb:
nil
irb(main):032:0> $cb
nil
irb(main):033:0> b
ArgumentError: tried to create Proc object without a block
from (irb):29:in new' from (irb):29:inb’
from (irb):33

It seems that for a formal proc parameter there is a default value of nil,
resulting in a reference to nil rather than the ArgumentError one might expect
when it isn’t given.

Thinking about it, you might simply be better off for the purpose of callbacks
taking the callback function as a normal argument; this way you can not
specify
a default of nil if you don’t want that, or you could give your own default.

def a§
$cb = p
end

a(lambda do |arg| p arg.inspect end)

hope this helps
-kyle


http://mas.cs.umass.edu/~rawlins

There is no friend
anywhere.


Web: http://hdl.handle.net/1030.37/1.1
Phone: +33 (0) 4 92 27 74 17


(Dirk Detering) #5

Hello, I’m new to ruby, coming from Python (et al).

Therefore my question:

Jean-Hugues ROBERT schrieb:

The main drawback is that optional parameters become mandatory when
one want to use Proc object instead of block:
def err( msg = “Err”, b = nil, &sb )
b = sb if sb
b( msg)
end
a = Proc.new do |x| print x end
err do |x| print x end
err “Hello” do |x| print x end
err “Hello”, a
err( , a) # Not in Ruby syntax…

This seems to indicate me that 'named parameters’
are missing, which are possible in Python
(and do attract me)?

With that the last line would read:
err (b=a)

Ok, in Ruby there should be an alternative syntax,
as this one causes another result (and is supposed to).

Bye
Det


(Evan Martin) #6

You can simulate named parameters with a hash:

def foo(params)
p params[:rar]
p params[:blah]
end

foo(:blah => “hello”, :rar => “meow”)

but yes, I agree Python is nicer in this respect.

···

On Thu, Jun 13, 2002 at 08:20:11PM +0900, Dirk Detering wrote:

This seems to indicate me that 'named parameters’
are missing, which are possible in Python
(and do attract me)?

With that the last line would read:
err (b=a)

Ok, in Ruby there should be an alternative syntax,
as this one causes another result (and is supposed to).


Evan Martin
martine@cs.washington.edu
http://neugierig.org


(Jean-Hugues ROBERT) #7

Hello,

Hello, I’m new to ruby, coming from Python (et al).

Therefore my question:

Jean-Hugues ROBERT schrieb:

The main drawback is that optional parameters become mandatory when
one want to use Proc object instead of block:
def err( msg = “Err”, b = nil, &sb )
b = sb if sb
b( msg)
end
a = Proc.new do |x| print x end
err do |x| print x end
err “Hello” do |x| print x end
err “Hello”, a
err( , a) # Not in Ruby syntax…

This seems to indicate me that 'named parameters’
are missing, which are possible in Python
(and do attract me)?

With that the last line would read:
err (b=a)

Ok, in Ruby there should be an alternative syntax,
as this one causes another result (and is supposed to).

No such thing yet. It is planned however.

In the mean time, the idiom seems to be to use a hash, with some
help from Ruby:

a( x,y, :foo => “hello”, :bar => “world”)
which is syntax sugar for:
a( x, y, {:foo => “hello”, :bar => “world”}).

a() needs to be something like:
def a( x, y, opt = nil )
foo = (opt && opt.include? :foo) ? opt[:foo] : <def_foo>
bar = (opt && opt.include? :bar) ? opt[:bar] : <def_bar>
xxx
end

BTW: I proposed a syntax sugar for hashes:
a( x, y, foo: “hello”, bar: “world”)
eqv a( x, y, :foo => “hello”, :bar => “world”) # already sugar
eqv a( x, y, {:foo => “hello”, :bar => “world”}) # final

On the callee side, one could enjoy:
def a( x, y, foo:, far: “Hello”) # foo defaults to nil, bar to “Hello”

Still an open subject I guess.

Yours,

Jean-Hugues

···

At 20:20 13/06/2002 +0900, you wrote:


(Dossy) #8

Does Ruby really automagically coerce that parameter list into
a Hash?

PoLS for me would have been to write:

foo( { :blah => "hello", :rar => "meow" } )

The shorthand you wrote where the parameters automagically
get coerced into a Hash is definitely the desirable behavior,
but at first glance I’d have to ask: “what kind of Object will
the #foo method be receiving?” and guess …

(I guess I can take out all those unecessary {}'s in my code
now … I probably should have tried without to see if it’d
work, but then I’d be surprised if it did …)

What about this, though:

foo :blah => "hello", :rar => "meow"

Does that work? Yes, it does, cool. But:

foo { :blah => "hello", :rar => "meow" }

That doesn’t work. (I can see why: am I passing a block, or a
Hash object?) This is the error I get (on Ruby 1.6.7):

irb(main):020:0> foo { :blah => "hello", :rar => "meow" }
SyntaxError: compile error
(irb):20: parse error
foo { :blah => "hello", :rar => "meow" }
              ^
        from (irb):20

So, is best-practice to let the Ruby parser do the magic and
create a new Hash array without using the {} notation?

– Dossy

···

On 2002.06.14, Evan Martin martine@cs.washington.edu wrote:

On Thu, Jun 13, 2002 at 08:20:11PM +0900, Dirk Detering wrote:

This seems to indicate me that 'named parameters’
are missing, which are possible in Python
(and do attract me)?

With that the last line would read:
err (b=a)

Ok, in Ruby there should be an alternative syntax,
as this one causes another result (and is supposed to).

You can simulate named parameters with a hash:

def foo(params)
p params[:rar]
p params[:blah]
end

foo(:blah => “hello”, :rar => “meow”)

but yes, I agree Python is nicer in this respect.


Dossy Shiobara mail: dossy@panoptic.com
Panoptic Computer Network web: http://www.panoptic.com/
“He realized the fastest way to change is to laugh at your own
folly – then you can let go and quickly move on.” (p. 70)


(Mike Depot) #9

I would like to see Ruby support better named parameter passing as well. I
guess the hash coersion is nice, but I long for something more… I really
think there is an oppourtunity to let Ruby do something that other languages
fall sort on, and that could be very good for the future of the language.

IMHO using named parameters gives you some great advantages in a number of
situations. At the moment, these stand out in my mind:

  1. The ability to pass parameters to a function in any arbitrary order.
  2. The ability to arbitrarily add parameters and change which parameters are
    required vs optional without being forced to change the calling interface to
    the function itself. (Great for reverse compatibility of public interfaces!)

The problem with most languages (at least those I’ve worked with) is that
named parameter passing is not supported as part of the language itself.
Typically, from the perspective of the language, you just pass a single
value to the function, which happens to be some sort of hash structure or
object containing the named parameter list. It then falls on the programmer
to manually write code to:

  1. Check that the parameters which are required were actually passed.
  2. Set defaults values for parameters that were optional.
  3. Complain about any invalid parameters that were passed

With the more traditional parameter passing scheme, the parser itself checks
all those things for you. It assignes passed values to the variables
defined in the function definition, it can warn you if too many or not
enough arguments were passed, and the parser itself can set defaults for
variables that are optional (assuming the case where you define defaults in
your function definition). The parser is able to do all this because the
syntax itself allows for the required information to be included in the
function definition itself.

It would be great if those same abilites existed for values passed in a
named parameter fashin. That is syntax existed that would allow the
function definition itself to contain:

  1. What parameters names are valid
  2. Of those that are valid, which are required vs optional
  3. What to use for defaults for optional parameters

With this info in the function definition, you could get all the advantages
of using named parameters, yet the parser itself could take care of all the
validation that now has to be checked manually in code.

At one point I read something that said Matz was thinking of building
support for named parameter passing into the next version of the language.
( I admit that was quite a while ago.) Is something like the above possible
without breaking reverse compatibility? I don’t know, but sure would like
to see a discussion on it…

Mike Depot

···

You can simulate named parameters with a hash:

def foo(params)
p params[:rar]
p params[:blah]
end

foo(:blah => “hello”, :rar => “meow”)


(Dave Thomas) #10

Dossy dossy@panoptic.com writes:

So, is best-practice to let the Ruby parser do the magic and
create a new Hash array without using the {} notation?

That’s the idiom.

Dave