Как выразить этот тип данных в Ruby?

#ruby #types #ruby-3

Вопрос:

У меня есть этот код preudo, который описывает тип

     type MyType1 = {
        type: :type1,
        field1: number,
        field2: any
    } | {
        type: :type2,
        field3: string
    } | {
        type: :type4,
        field4: SomeOtherType
    } | {
        type: :type5,
        field5: string,
        field6: integer,
        field7: float
    } | {
        type: :type6
    }
 

Я выразил это так в Ruby:

     class MyType1
        attr_reader :type, :field1, :field2, :field3, :field4, :field5, :field6, :field7

        def init_with_type_1(field1:, field2:)
          @type = :type1
          @field1 = field1
          @field2 = field2
        end

        def init_with_type_2(field3:)
          @type = :type2
          @field3 = field3
        end

        # and so on...

    end
 

Есть ли лучший, более идиоматичный, более простой способ?

Я не рассматриваю возможность использования сторонних драгоценных камней и библиотек.

Комментарии:

1. Лучшим и более простым способом было бы использовать только функции и карты. Но это не обязательно более идиоматично, потому что может быть неудобно писать функциональный код на Ruby.

2. В Ruby я бы, вероятно, определил суперкласс Type с подклассами Type1 и Type2 т. Д.

3. Модель стратегии?

Ответ №1:

Я бы сделал это вот так:

 Type1 = Struct.new(:field1, :field2)
Type2 = Struct.new(:field3)

class MyType1
  def initialize(some_type_item)
    @data = some_type_item
  end
  def self.init_with_type_1(f1, f2)
    self.new(Type1.new(f1,f2))
  end
  def type
    @data.class # Or @data.class.name, if you prefer
  end
  def field1
    # Raises exception, if we don't have Type1 
    @data.field1
  end
end
 

Я закодировал это явно, но часть работы (например, определение поля1 и т. Д.) Можно было бы упростить (меньше печатать) с помощью Forwardable модуля из стандартной библиотеки Ruby. Кроме того, init_with_type методы могут быть сгенерированы с помощью цикла, что может упростить задачу, если у вас действительно много разных типов, которые вы хотите сохранить здесь.

Я смоделировал это близко к вашему первоначальному решению, хотя я спрашиваю себя, почему вы вводите класс MyType1 для этого; после определения Type1, Type2 и т. Д. Я бы Просто сделал

 myTypeVar = Type2.new(4711)
puts "myTypeVar is a #{myTypeVar.class}"
puts myTypeVar.field3 # OK
puts myTypeVar.field1 # Exception!
 

Комментарии:

1. даже если это представляет MyType1 собой , это не упрощает вещи, а чрезмерно усложняет их.

2. @Khinkala2 : На самом деле, именно поэтому я написал ниже, как бы я подошел к этой проблеме. ИМО, изобретать MyType1 здесь не нужно. Если все типы имеют общие определенные функции, возможно, вам лучше использовать a Module в качестве миксина. Если мы не увидим, как вы планируете использовать этот тип, его трудно рекомендовать. Кстати: Мой подход выглядит сложным, если вы вручную введете все методы. Я сделал это только для ясности, но если у вас много вложенных типов, вы будете генерировать их с помощью цикла, и в итоге это уменьшит количество кода. Например, вы можете автоматически сгенерировать методы поля n и init_with_type n_ .