如果无法在编译时确定数据类型,如何检测 Crystal 中的数据类型?

How to detect data types in Crystal if they cannot be determined at compile-time?

我正在用 for the Ethereum RLP 标准编写递归编码器。

我要做的是获取任何要编码的传入数据 blob 并确定其类型。现在,为了简单起见,我可以忽略无效类型。

RLP 编码的有效类型是二进制 Bytes、字符串 String 或字符串列表 Array(String)。到目前为止,一切顺利,我编写了三种允许三种数据类型的方法:

module Rlp
  # rlp-encodes binary data
  def self.encode(b : Bytes)
    return "binary #{typeof(b)} #{b}"
  end

  # rlp-encodes lists data
  def self.encode(l : Array(String))
    return "listsy #{typeof(l)} #{l}"
  end

  # rlp-encodes string data
  def self.encode(s : String)
    return "strngy #{typeof(s)} #{s}"
  end
end

然而,现在这里有一些深度,因为数组可以嵌套。因此,这个编码器是递归的。给定一个 Array(String) 它给它一些前缀并用 Rlp.encode(s : String) 编码 String。现在,嵌套数组的逻辑是根据需要经常调用 .encode 来对所有内容进行编码。但是,我没有想清楚如何在编译时确定类型。

例如:

 79 | Rlp.encode([["a", "b", "c"], ["cow", "dog", "cat"]])
          ^-----
Error: no overload matches 'Rlp.encode' with type Array(Array(String))

是的,因为我没有任何 self.encode(l : Array(Array(String))) 实现,我不知道这里的嵌套深度可能会实现所有可能的情况。

我尝试实现不指定数据类型的不太严格的包装方法 self.encode(data),但是,我基本上无法对 data 做任何事情,因为编译器会根据我的用法暗示数据类型,即:

# rlp-encodes _any_ data
def self.encode(data)
  if typeof(data) == Int32
    return Bytes.new data
  elsif typeof(data) == Char
    return String.new data
  end
end

传递 Int32 类型的 32 会导致:

 45 | return String.new data
Error: no overload matches 'String.new' with type Int32

即使不会使用 Int32 类型的数据调用此代码。我不确定如何在这里进行。在编译时不可知的情况下,是否可以更智能地了解所使用的类型?

理想情况下,我会接受 任何 数据作为输入并自己处理不同的情况。

如果您如下声明 Array 的 Rlp.encode,编译器将负责为不同类型的数组实例化该方法。

module Rlp
  # rlp-encodes lists data
  def self.encode(l : Array)
    return "listsy #{typeof(l)} #{l}"
  end
end

也许这里转换所有值有点笨拙.to_s

# rlp-encodes lists data
def self.encode(l : Array)
  encode(l.flatten.compact.map(&.to_s))
end

def self.encode(l : Array(String))
  return "listsy #{typeof(l)} #{l}"
end