Crystal: NamedTuple#to_h fails when a tuple is empty

Created on 2 Jul 2016  路  8Comments  路  Source: crystal-lang/crystal

def foo(**options)
  options.to_h
end

foo # Syntax error in expanded macro: macro_XXX: for empty hashes use '{} of KeyType => ValueType'

I would like to fix it, but I don't know what to_h should return in this case. Thank you.

bug stdlib

Most helpful comment

But that won't allow you to check existence of a key. Whatever the key of the type you want. Until we support object i would say we should raise or even not compile.

All 8 comments

Oh, it's easy, the method should used NamedTuple.new(...) instead of {...}.

But what would be the type of the resulting Hash in this case? {} of String => Nil perhaps

Oh... it's to_h, I was confused. Mmm... maybe we should check for this case. I think the Hash should be {} of NoReturn => NoReturn.

Probably the error is correct, using to_h on an empty named tuple doesn't make much sense anyway.

But that won't allow you to check existence of a key. Whatever the key of the type you want. Until we support object i would say we should raise or even not compile.

FYI, my use case:

struct NamedTuple
  def merge(other)
    T.from(to_h.merge(other.to_h))
  end
end

class C
  @options : NamedTuple(i: Int32, s: String, b: Bool)
  DEFAULT_OPTIONS = {i: 1, s: "str", b: false}

  def initialize(**options)
    @options = DEFAULT_OPTIONS.merge(options)
  end
end

If to_h occurs a compile error whenever a tuple is empty, the above code is useless. I'll find another solution. Thank you.

I personally don't think modelling C that way is a good idea. You should have variables @i, @s and @b in C, and make the initialize accept those as named arguments. All those named tuples and hashes will go away, and the code will be simpler and faster.

@asterite Thank you for your advice! I like named arguments but it's not suitable this time :). By the way, I defined to_h? method for my problem like:

struct NamedTuple
  def merge(other)
    if h = to_h?
      if other = other.to_h?
        T.from(h.merge(other))
      else
        self
      end
    else
      raise ArgumentError.new if other.to_h?
      self
    end
  end

  def to_h?
    {% unless T.empty? %}
      to_h
    {% end %}
  end
end

It's just FYI!

Was this page helpful?
0 / 5 - 0 ratings