Crystal: allow optional block parameter

Created on 20 Jul 2015  路  11Comments  路  Source: crystal-lang/crystal

Allow blocks to be optional instead of requiring overloaded methods.
def foo(&block = nil)

The current way of using overloads causes unnecessary code duplication:
def foo() { }
def foo(&block)

draft compiler

Most helpful comment

@0x000def42 nope. This has never been implemented, as nice as it would be to have. Currently the only real solution I know of is still just to use overloaded methods.

All 11 comments

On a more detailed request, having support for block_given? would be nice to avoid having blunt overloads when a block is optional but implementations are identical otherwise. For instance:

def foo
  foo {}
end

def foo
  yield
end

The following would be nicer:

def foo
  yield if block_given?
end

This is really hard to implement, mostly becuase a non-captured block is not an object that you can pass around.

@asterite maybe it can be implemented by expanding the non typed def in two when block_given? is used in the body. One with yield and replacing the block_given? with true and another without yield and replacing the block_given? with false. All this before the type inference.

Can we solve this using macros?

def foo
  {% if @call.block? %}
    yield
  {% else %}
    raise "meh"
  {% end %}
end

@call would make the method a macro method just like @type so it's instantiated for each call and then refer to the call it was instantiated by. Regular methods win if there are any that match. @call having AST access to the original call arguments could probably also allow some very interesting things.

Maybe we could make it so that block_given? would be a special call that the language would know about. Then for a method that uses this the compiler would generate two methods, one where block_given? would be replaced by true and marked as yielding, and the other as not, or something like that. Since this might be common, writing if block_given? rather than using the macro language is less noisy.

But I'll have to think about it a bit more.

Or simply having if block_given? be something special, I think that could work fine too and also be easier to implement.

What are current examples in the standard library or in shards that could benefit from this, and how?

I was thinking about something like this

def pop(&block = {
  raise IndexError.new
})
  if @size == 0
    yield
  else
    @size -= 1
    value = @buffer[@size]
    (@buffer + @size).clear
    value
  end
end

In 2020 exists nice solution for it?

@0x000def42 nope. This has never been implemented, as nice as it would be to have. Currently the only real solution I know of is still just to use overloaded methods.

Currently the only real solution I know of is still just to use overloaded methods.

I honestly don't see anything wrong with that. Overloads are fine.

Overloads just look messy, but they work

Was this page helpful?
0 / 5 - 0 ratings

Related issues

oprypin picture oprypin  路  3Comments

oprypin picture oprypin  路  3Comments

Sija picture Sija  路  3Comments

lbguilherme picture lbguilherme  路  3Comments

jhass picture jhass  路  3Comments