Can you detect if a method takes a block argument?

Hi all, hope everybody had a great new year - it is snowing outside for
the first time this winter :slight_smile:

Sorry if this is common knowledge, I have done some searching of archives
but cannot find information…
Is there a way to tell whether or not a method takes a block argument?
The sort of thing that I want to do is write the Client class below (from
the pseudocode example given):

class Server
def methodWithNoBlock(arg)
return 1
end

def methodWithBlock(arg, &blk)
	blk.call(1)
	blk.call(2)
end

end

Client is the class that I will write - sort of a delegator with knobs

on
client = Client.new(Server.new)

If you call the method “normally” - then everything behaves as expected

client.methodWithNoBlock => 1

If you call the method with a block and it doesn’t take a block, then

the method’s return value

is passed as block argument.

client.methodWithNoBlock { |arg|
# here arg is 1, and the method returns nil
}

If you call a method that takes a block, and you don’t pass one, then

the method’s return value

is an array of the block arguments:

client.methodWithBlock => [1, 2]

If you call a method that takes a block, and you do pass one, then

everything behaves as expected
client.methodWithBlock { |arg|
# first time arg is 1, second time it is 2
# method returns nil
}

The bit that I don’t know how to do is:

  1. If client detects that the requested method takes a block argument, it
    should:
    a) pass the block given to it (if there was one), or
    b) create a dummy block that fills a return array, because the caller
    didn’t provide a block.

Possible? Sensible? etc.

Cheers for any help you can give,

···


Martin Hart Tel: +44 (0) 1582 618468
Arnclan Fax: +44 (0) 1582 619596
Union Street, E-mail: martin@zsdfherg.com
Dunstable, Beds LU6 1EX

block_given?

Dalibor Sramek http://www.insula.cz/dali | In the eyes of cats,
dalibor.sramek@insula.cz | all things belong to cats.

···

On Wed, Jan 08, 2003 at 10:28:12PM +0900, Martin Hart wrote:

Is there a way to tell whether or not a method takes a block argument?

Is there a way to tell whether or not a method takes a block argument?

Briefly, no.

For example, Array#sort can be used with or without a block.

Guy Decoux

I think the question is, in fact, meaningless, because all methods can
take a block, but they might not do anything with it.

gordon hgs 56 %> irb
irb(main):001:0> print(3) { puts “hi there” }
3nil
irb(main):002:0> print(3) {|x| puts “hello #{x}” }
3nil
irb(main):003:0> print(3)
3nil
irb(main):004:0>

The inverse problem, “has a block been passed?”, is answered by
block_given?

    Hugh
···

On Wed, 8 Jan 2003, Martin Hart wrote:

Is there a way to tell whether or not a method takes a block argument?

Quick shot, suppose ruby wizards can make this still better.

def use_block?(invoke)
catch (:block) do
eval "#{invoke} do
throw :block, true
end"
end or false
end

pass the call you want to check along with arguments as string.

use_block?(“print(‘3\n’)”)
3
=> false
use_block?("[1,2,3].sort")
=> true

-Martin

Well, if I've well understood he want to know if the method that he call
take a block not if the current method was called with a block.

Guy Decoux

···

On Wed, Jan 08, 2003 at 10:28:12PM +0900, Martin Hart wrote:

Is there a way to tell whether or not a method takes a block argument?

block_given?

Is there a way to tell whether or not a method takes a block argument?

Briefly, no.

Slightly less briefy, yes.

For example, Array#sort can be used with or without a block.

i.e. any method can take a block. So the answer is always yes. I
think Martin will have to reconsider his design.

Gavin

···

On Thursday, January 9, 2003, 12:50:47 AM, ts wrote:

And the real question - does method foo do anything with a block
if one is given - seems to be unanswerable.

Gavin

···

On Thursday, January 9, 2003, 1:05:52 AM, Hugh wrote:

On Wed, 8 Jan 2003, Martin Hart wrote:

Is there a way to tell whether or not a method takes a block argument?

I think the question is, in fact, meaningless, because all methods can
take a block, but they might not do anything with it.
[…]
The inverse problem, “has a block been passed?”, is answered by
block_given?

On 1/8/03, 1:50:47 PM, ts decoux@moulon.inra.fr wrote regarding Re: Can
you detect if a method takes a block argument?:

Is there a way to tell whether or not a method takes a block argument?

Briefly, no.

For example, Array#sort can be used with or without a block.

Thanks for the information Guy,

So the only way to accomplish what I want is to have all of the methods
that I might call be able to take a block and work sensibly whether or
not it is given, (like Array#sort). That’s not something that I want to
do - I think it will pollute the design too much. Guess I’ll rethink :slight_smile:

PS - When you say “Briefly, no” (as opposed to just “No”), does that mean
that it might be possible (without hacking Ruby itself I mean) :slight_smile:

Cheers
Martin

use_block?("[1,2,3].sort")

=> true

Re-read his message

The bit that I don't know how to do is:
1. If client detects that the requested method takes a block argument, it
should:
a) pass the block given to it (if there was one), or
b) create a dummy block that fills a return array, because the caller
didn't provide a block.

You really *always* want to call Array#sort with a block ?

Guy Decoux

Nice. The only problem is that if the function has side effects
data may be (adversely?) affected.

But there is a further problem with the whole question, I feel.
If you know the method takes a block, you need to know what it
does with the block – i.e semantics. Does it use the block once or
many times, etc? So to be able to do anything useful with the
knowledge that “this method takes a block”, you need more information
than that. If you have this further information you will already
know that it does/doesn’t use a block.

In other words, how can the information that a method uses a block
be sufficient knowledge to use that method correctly?

    Hugh
···

On Wed, 8 Jan 2003, Martin Weber wrote:

Quick shot, suppose ruby wizards can make this still better.

def use_block?(invoke)
catch (:block) do
eval "#{invoke} do
throw :block, true
end"
end or false
end

Hi –

Quick shot, suppose ruby wizards can make this still better.

def use_block?(invoke)
catch (:block) do
eval "#{invoke} do
throw :block, true
end"
end or false
end

pass the call you want to check along with arguments as string.

use_block?(“print(‘3\n’)”)

Don’t deprive yourself of the pleasures of #puts :slight_smile:

3
=> false

use_block?("[1,2,3].sort")
=> true

I’m not sure what you gain here, since you only learn after the fact
that a method had to take a block. You could call the method
again… but it’s very possible that the first call will have changed
the program’s state. I think you’d be better off with an underlying
design that didn’t depend on this. Can you not just document your
methods so that the code that invokes them knows what to do?

David

···

On Wed, 8 Jan 2003, Martin Weber wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

What about functions that take an optional block?

def optblock(&block)
return 5 if block_given?
4
end

optblock {} #=> 5
optblock #=> 4
use_block?(“optblock”) #=> 5

···

On Wednesday, 8 January 2003 at 23:30:51 +0900, Martin Weber wrote:

Quick shot, suppose ruby wizards can make this still better.

def use_block?(invoke)
catch (:block) do
eval "#{invoke} do
throw :block, true
end"
end or false
end


Jim Freeze

If only one could get that wonderful feeling of accomplishment without
having to accomplish anything.

i.e. any method *can* take a block. So the answer is always yes.

You can do that ?

The bit that I don't know how to do is:
1. If client detects that the requested method takes a block argument, it
should:
a) pass the block given to it (if there was one), or
b) create a dummy block that fills a return array, because the caller
didn't provide a block.

See b)

Try to call Array#sort with a dummy block just to see the result ...

Guy Decoux

PS - When you say "Briefly, no" (as opposed to just "No"), does that mean
that it might be possible (without hacking Ruby itself I mean) :slight_smile:

No, this was just because I was sure that someone else will respond
"yes" :slight_smile: and his response will not solve your problem :-))

Guy Decoux

Wow - thanks for all the responses, I’m overwhelmed with information now
:slight_smile:

Ok, my design needs rethinking, but Martin Weber’s code snippet made me
think a bit.

I want a program that will run in “distributed” or “local” mode without
changing any code. While this is fairly straightforward to achieve in
Ruby (delegation with an optional XMLRPC layer or some such), I also need
to optimise various types of method call. For example:

A “server” has a method loadAllItems - (there could be lots of items).
A “client” calls it.

Now in a “local” environment, it might be ok for the client to wait for
the server to build a huge array of items and return it. In the
"distributed" environment it probably won’t be.

So, what I thought I would do is:

server.loadItems(loadCriteria) { |item|
# Do something with item (perhaps the block receives a chunk of items
rather than just 1)
}

By using delegation, this will work regardless of where the “server” is
(same process or other side of world).

So to answer Guy’s question:

You really always want to call Array#sort with a block ?

In the case of my design (if you can call it that :-), the block doesn’t
really have any semantics associated with the message call - it’s just an
alternative way to receive the method’s return value/or partial return value.

[(D Black)]

Can you not just document your methods so that the code that invokes
them knows what to do?

Yes -

  • loadAllItems never takes a block and returns the lot
  • loadItemsInChunks can always take a block pass it a chunk of items
  • In distributed mode, there is a proxy layer, in local mode there isn’t.

Perhaps I am trying to be too clever? What I think I want is to function
"sensibly" regardless of whether a block is passed. My definition of
sensible is obviously different from everyone elses :slight_smile:

I have been persuaded that the design is bad - I’ll rethink.

Finally, on the topic of snow,

  1. Its cold and wet
  2. Its too bright to look at
  3. Bah, Humbug :slight_smile:

Cheers,
Martin