Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to declare a method with block as default value?

Tags:

ruby

lambda

block

I want to write a method which takes a block and if no block given it should use a default block. So I want to have something like this:

def say_hello(name, &block = ->(name) { puts "Hi, #{name}" })
  # do something
end 

But when I'm trying to do so I'm getting the syntax error.

I know I can deal with my problem using block_given?. But I am interested in first approach. Am I missing something or this is just not possible?

like image 331
user2422869 Avatar asked Jan 11 '23 19:01

user2422869


2 Answers

Some answers suggest using block_given?, but since there is no possibility that a block would be nil or false when it is given, you can simply use ||=.

def say_hello(name, &block)
  block ||= ->(name){puts "Hi, #{name}"}
  # do something
end
like image 144
sawa Avatar answered Jan 28 '23 04:01

sawa


You cannot declare a default block in the method definition, however you can use a little trick to use a custom block if none is given.

def say_hello(name)
  block = block_given? ? Proc.new : ->(name) { puts "Hi, #{name}" }
  block.call(name)
end

# This example uses a custom block
say_hello('weppos') { |name| puts "Hello, #{name}!" }
# => Hello, weppos!

# This example fallbacks to the default
say_hello('weppos')
# => Hi, weppos!

Let me explain it a little bit. Let's start from a more readable version.

def say_hello(name, &block)
  block = block ? block : ->(name) { puts "Hi, #{name}" }
  block.call(name)
end

You define the method to accept a block, then you check if block is defined. If not, you assign a custom block. Finally, you execute the block.

Let's enhance it a little bit. You can use block_given? to check if a block is passed

def say_hello(name, &block)
  block = block_given? ? block : ->(name) { puts "Hi, #{name}" }
  block.call(name)
end

This also allows you to skip the declaration of the block (&block) in the method definition.

def say_hello(name)
  if block_given?
    yield name
  else
    # This is rendundant, but it's for clarity
    block = ->(name) { puts "Hi, #{name}" }
    block.call(name)
  end
end

But, at this point, you can also use the Proc.new to assign the block to a variable.

def say_hello(name)
  block = block_given? ? Proc.new : ->(name) { puts "Hi, #{name}" }
  block.call(name)
end

As a final word, I'm trying to understand when this approach would make sense. In most cases, you can probably wrap the code in a class or module and pass it as argument. It's probably better.

like image 27
Simone Carletti Avatar answered Jan 28 '23 05:01

Simone Carletti