Подтвердить что ты не робот

Практическое использование для структурных типов?

Структурные типы - один из тех "вау, круто!" функции Scala. Тем не менее, для каждого примера я могу думать о том, где они могут помочь, неявные преобразования и динамическая композиция mixin часто кажутся лучшими совпадениями. Каковы некоторые общие применения для них и/или советы, когда они подходят?

4b9b3361

Ответ 1

Помимо редкого случая классов, которые предоставляют один и тот же метод, но не связаны друг с другом и не реализуют общий интерфейс (например, метод close() - Source, для одного, не распространяется Closeable), Я не считаю нужным использовать структурные типы с их нынешним ограничением. Однако, если они были более гибкими, я мог бы написать что-то вроде этого:

def add[T: { def +(x: T): T }](a: T, b: T) = a + b

который будет аккуратно обрабатывать числовые типы. Каждый раз, когда я думаю, что структурные типы могут мне помочь, я ударил эту стену.

ИЗМЕНИТЬ

Однако, я не знаю, какие типы конструкций я использую сам, компилятор использует его для обработки анонимных классов. Например:

implicit def toTimes(count: Int) = new {
  def times(block: => Unit) = 1 to count foreach { _ => block }
}

5 times { println("This uses structural types!") }

Объект, полученный из (неявного) toTimes(5), имеет тип { def times(block: => Unit) }, т.е. структурный тип.

Я не знаю, делает ли это Scala для каждого анонимного класса - возможно, это так. Увы, это одна из причин того, почему моя супница моя библиотека работает медленно, поскольку структурные типы используют отражение для вызова методов. Вместо анонимного класса следует использовать реальный класс, чтобы избежать проблем с производительностью в моей сутенере.

Ответ 2

Структурные типы - очень классные конструкции в Scala. Я использовал их для представления нескольких несвязанных типов, которые разделяют атрибут, по которому я хочу выполнить общую операцию без нового уровня абстракции.

Я слышал один аргумент против структурных типов от людей, которые строги относительно архитектуры приложения. Они считают опасным применять общую операцию по типам без ассоциативного признака или родительского типа, потому что тогда вы оставляете правило того типа, которым метод должен применяться к открытому. Пример Daniel close() - это пятно, но что, если у вас есть другой тип, который требует другого поведения? Кто-то, кто не понимает архитектуру, может использовать ее и вызывать проблемы в системе.

Ответ 3

Я думаю, что структурные типы - одна из этих функций, которые вам не нужны, но когда вам это нужно, это очень помогает. Одна область, где действительно сильные структурные типы, - это "дооснащение", например. когда вам нужно склеить несколько программ, у вас нет исходного кода и которые не предназначены для повторного использования. Но если вы часто используете структурные типы, вы, вероятно, ошибаетесь.

[изменить]

Конечно, имплициты часто бывают дороги, но бывают случаи, когда вы не можете: представьте, что у вас есть изменяемый объект, который вы можете модифицировать с помощью методов, но который скрывает важные части его состояния, своего рода "черный ящик" ". Тогда вам нужно как-то работать с этим объектом.

Другим вариантом использования структурных типов является то, что код опирается на соглашения об именах без общего интерфейса, например. в машинный код. В JDK мы также можем найти такие вещи, как пара StringBuffer/StringBuilder (где общие интерфейсы Appendable и CharSequence являются общими). ​​

Ответ 4

Структурные типы дают некоторые преимущества динамических языков статически связанному языку, в частности, свободному соединению. Если вы хотите, чтобы метод foo() вызывал методы экземпляра класса Bar, вам не нужен интерфейс или базовый класс, который является общим как для foo(), так и для Bar. Вы можете определить структурный тип, который принимает foo() и чей Bar не имеет понятия существования. Пока Bar содержит методы, которые соответствуют сигнатурам структурного типа, foo() сможет вызвать.

Это здорово, потому что вы можете поместить foo() и Bar в разные, полностью несвязанные библиотеки, то есть без общего ссылочного контракта. Это уменьшает требования к сцеплению и тем самым дополнительно способствует ослаблению сцепления.

В некоторых ситуациях структурный тип может использоваться как альтернатива шаблону Adapter, поскольку он предлагает следующие преимущества:

  • Идентификатор объекта сохраняется (нет отдельного объекта для экземпляра адаптера, по крайней мере, на семантическом уровне).
  • Вам не нужно создавать экземпляр адаптера - просто передайте экземпляр Bar в foo().
  • Вам не нужно реализовывать методы-обёртки - просто объявите требуемые подписи в структурном типе.
  • Структурный тип не должен знать фактический класс экземпляра или интерфейс, в то время как адаптер должен знать Bar, чтобы он мог вызывать его методы. Таким образом, для многих реальных типов может использоваться один структурный тип, тогда как с адаптером необходимо кодировать несколько классов - по одному для каждого фактического типа.

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