Converting a nilable generic argument to its non-nil version

Wrote up a little blog post on something I learned today from the standard library for my own needs.

3 Likes

Nice blog post!

You might enjoy this one then: It's a typeof magic - The Crystal Programming Language

3 Likes

@asterite I’m curious if you know the reason behind the problem I’m experiencing.

So my blog post details how to make a method migrate a generic from T to T! but now I’m working on going from A to B with a map method that will work for nilable and non-nilable types (using an overlaod for the mapping).

I’m using a macro in one method to do a if-else based on whether or not the generic type is nilable. I found out that if I try to reference a block argument in the macro it fails but referencing regular arguments works as expected.

Here’s a simple example showing a method working that references a regular parameter and a method that fails to compile since it references the block argument. I got around this in my code by assigning the block to a temporary variable and referencing that variable instead.

def foo(x : T) : String forall T
  {% if T.nilable? %}
  "nilable: #{x}"
  {% else %}
    "non-nilable: #{x}"
  {% end %}
end

foo("abc")
foo("def".as(String?))

def bar(x : T, &block : -> String) : String forall T
  {% if T.nilable? %}
  "nilable: #{block.call}"
  {% else %}
    "non-nilable: #{block.call}"
  {% end %}
end

bar("abc") { "hello" }
bar("def".as(String?))  { "goodbye" }

Do you know what the problem is here? Or maybe it’s just a bug

Reduced:

def bar(&block : -> String) : Nil
  {% begin %}
    block.call
  {% end %}
end

bar { "hello" }
 > 1 | 
 > 2 |     block.call
           ^----
Error: undefined local variable or method 'block' for top-level

Sure just seems like a bug to me? Of course in this example you can use yield which doesn’t have this problem.

1 Like

Nice post! But couldn’t you work with a non-nil type as generic argument instead, and add the Nil type inside? Something like

class Parser(T)
  getter input : T?

  ...
end

The example in the blog post was very very simple. In my real code, I’m holding onto a proc that will parse the input and allowing multiple method calls that affect the return type of the proc. So going from nilable to non-nil, string to number, etc

1 Like