Как определить типы данных в Crystal, если они не могут быть определены во время компиляции? - PullRequest
0 голосов
/ 20 января 2020

Я пишу рекурсивный кодировщик в для стандарта 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) он дает ему некоторый префикс и кодирует String с помощью Rlp.encode(s : String). Теперь логика c для наличия вложенного массива будет вызывать .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

Передача 32 типа Int32 приведет к:

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

Даже если этот код не будет нельзя вызывать с данными типа Int32. Я не уверен, как действовать здесь. В любом случае, нужно ли быть более умным в отношении типов, используемых во время компиляции c во время компиляции?

В идеале я бы просто принял любые данные в качестве входных данных и сам обрабатывал различные случаи.

Ответы [ 2 ]

3 голосов
/ 20 января 2020

Если вы объявите Rlp.encode для массива следующим образом, компилятор позаботится о создании экземпляра этого метода для различных типов массивов.

module Rlp
  # rlp-encodes lists data
  def self.encode(l : Array)
    return "listsy #{typeof(l)} #{l}"
  end
end
0 голосов
/ 20 января 2020

Может быть, это немного сложновато для преобразования всех значений .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
...