Рекурсивный тип, не объединяющий себя - PullRequest
0 голосов
/ 07 мая 2019

Следующий код не компилируется с ошибкой:

type must be Tuple(Thing::Ish, Slice(UInt8)), not Tuple(Array(Array(Thing::Ish) | UInt8) | UInt8, Slice(UInt8))

Эти два типа кажутся мне эквивалентными ... и добавление .as(Ish) в нужном месте работает ... что мне не хватает? Почему эти типы не объединяются?

module Thing
    alias Ish = UInt8 | Array(Ish)

    def self.decode(bytes : Bytes) : {Ish, Bytes}
        case bytes[0]
            when 0x00..0x17
                {bytes[0], bytes + 1}
            when 0x18
                MultiItemDecoder.new(0x80, ->(x: Bytes) { Thing.decode(x) }).decode(bytes)
            else
                raise "unknown"
        end
    end

    class MultiItemDecoder(T)
        def initialize(@base : UInt8, @item_decoder : Bytes -> {T, Bytes})
        end

        def decode(bytes): {Array(T), Bytes}
            decode_some(bytes + 1, bytes[0])
        end

        def decode_some(bytes, n)
            items = n.times.map do
                item, bytes = @item_decoder.call(bytes)
                item
            end
            {items.to_a, bytes}
        end
    end
end

Ответы [ 2 ]

1 голос
/ 09 мая 2019

Это работает:

module Thing
  alias Ish = UInt8 | Array(Ish)

  def self.decode(bytes : Bytes) : {Ish, Bytes}
    case bytes[0]
    when 0x00..0x17
      {bytes[0], bytes + 1}
    when 0x18
      MultiItemDecoder.new(0x80, ->(x : Bytes) { Thing.decode(x) }).decode(bytes)
    else
      raise "unknown"
    end
  end

  class MultiItemDecoder(T)
    def initialize(@base : UInt8, @item_decoder : Bytes -> {T, Bytes})
    end

    def decode(bytes) : {Ish, Bytes}
      decode_some(bytes + 1, bytes[0])
    end

    def decode_some(bytes, n)
      items = n.times.map do
        item, bytes = @item_decoder.call(bytes)
        item.as(Ish)
      end
      {items.to_a.as(Ish), bytes}
    end
  end
end

Thing.decode(Bytes[1, 2, 3])

Дело в том, что Array(Array(Ish)) не является Array(Ish), потому что для этого оно должно быть Array(Array(Ish) | UInt8) (обратите внимание, что это массив объединения).

Все это сводится к тому, как вещи представлены в памяти.

Мой совет - избегать использования рекурсивных псевдонимов. Они не интуитивно понятны, и мы могли бы в конечном итоге удалить их из языка.

0 голосов
/ 07 мая 2019

Не видя код, который делает это на самом деле компилируемым и получает ошибку, self.decode хочет вернуть {Ish, Bytes}, что он делает в when 0x00..0x17.Но в 0x18 он вернет {Array(Ish), Bytes}.Это расширится до {Array(UInt8 | Array(Ish)), Bytes} (рекурсивно)

...