Черта, FunctionN или trait-inheriting-FunctionN в Scala?

У меня есть черта в Scala, который имеет отдельный метод. Назовите это Вычислимым, и отдельный метод, вычисляют (вход: Интервал): Интервал. Я не могу выяснить, должен ли я

  • Оставьте его как автономную черту с отдельным методом.
  • Наследуйтесь (Интервал => Интервал) и переименуйте, "вычисляют" для "применений".
  • Просто избавьтесь от Вычислимых и использования (Интервал => Интервал).

Фактор в пользу него являющийся чертой - то, что я мог полезно добавить некоторые дополнительные методы. Но конечно если бы они были все реализованы с точки зрения вычислить метода затем, то я мог бы просто выломать их в отдельный объект.

Фактором в пользу просто использования функционального типа является простота и то, что синтаксис для анонимной функции более краток, чем это для анонимного Вычислимого экземпляра. Но затем у меня нет способа отличить объекты, которые являются на самом деле Вычислимыми экземплярами от других функций, которые отображают Интервал на Интервал, но не предназначены, чтобы использоваться в том же контексте в качестве Вычислимых.

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

8
задан Willis Blackburn 18 June 2010 в 10:13
поделиться

4 ответа

Создание признака, наследующего от типа функции, может быть полезно по нескольким причинам.

  1. Ваш функциональный объект делает что-то особенное и неочевидное (и сложное для ввода), и вы можете параметризовать небольшие вариации в конструкторе. Например, предположим, что вы пишете трейт для выполнения запроса XPath к дереву XML. Функция apply скроет несколько видов работы по созданию механизма запросов XPath, но все же стоит реализовать интерфейс Function1 , чтобы вы могли запрашивать, начиная с целой группы различных узлов, используя map или flatMap .

  2. В качестве расширения №1 вы хотите выполнить некоторую обработку во время создания (например, проанализировать выражение XPath и скомпилировать его для быстрой работы), вы можете сделать это один раз заранее в конструкторе объекта (тогда как если вы только что каррированная функция Function без подкласса, компиляция может происходить только во время выполнения, поэтому она будет повторяться для каждого запроса.)

  3. Вы хотите передать функцию шифрования (тип Function1 [ String, String] ) как неявное, но не все Function1 [String, String] выполняют шифрование. Унаследовав от Function1 [String, String] и присвоив подклассу / признаку имя EncryptionFunction , вы можете гарантировать, что неявно будут передаваться только функции правильного подкласса. (Это неверно при объявлении Type EncryptionFunction = String => String .)

Надеюсь, это было ясно.

3
ответ дан 5 December 2019 в 12:55
поделиться

Если вы сделаете это чертой и по-прежнему хотите иметь возможность использовать упрощенный синтаксис функций, вы также можете дополнительно добавить неявное преобразование в тех местах, где вы хотите:

scala> trait Computable extends (Int => Int)
defined trait Computable

scala> def computes(c: Computable) = c(5)
computes: (c: Computable)Int

scala> implicit def toComputable(f: Int => Int) = new Computable { def apply(i: Int) = f(i) }
toComputable: (f: (Int) => Int)java.lang.Object with Computable

scala> computes( (i: Int) => i * 2 )
res0: Int = 10
8
ответ дан 5 December 2019 в 12:55
поделиться

Похоже, вы могли бы захотеть использовать структурный тип . Их также называют неявными интерфейсами.

Затем вы можете провести рефакторинг методов, которые в настоящее время принимают Computable , чтобы принять все, что имеет метод compute (вход: Int) .

1
ответ дан 5 December 2019 в 12:55
поделиться

Один из вариантов - определить тип (вы все еще можете называть его Computable), который на данный момент является Int=>Int. Используйте его всякий раз, когда вам нужны вычислимые вещи. Вы получите все преимущества наследования от Function1. Затем, если вы поймете, что вам нужно больше методов, вы можете изменить тип на другой признак.

Сначала:

type Computable = Int => Int

Потом:

type Computable = ComputableTrait // with its own methods.

Один из недостатков этого способа заключается в том, что тип, который вы определили, на самом деле не является новым типом, скорее это своего рода псевдоним. Так что пока вы не измените его на трейты, компилятор будет принимать другие функции Int => Int. По крайней мере, вы (разработчик) можете различать. Когда вы перейдете на трейты (и разница станет важной), компилятор обнаружит, когда вам нужен Computable, но есть Int => Int.

Если вы хотите, чтобы компилятор с первого дня отвергал другие Int => Int, то я бы рекомендовал использовать трейты, но расширять Int => Int. Когда вам понадобится вызвать его, вы все равно будете иметь более удобный синтаксис.

Другим вариантом может быть использование трейта и объекта-компаньона с методом apply, который принимает Int => Int и создает из него Computable. Тогда создание новых Computables будет почти так же просто, как написание обычных анонимных функций, но у вас останется проверка типов (которую вы потеряете при неявном преобразовании). Кроме того, вы могли бы без проблем подмешивать трейты (но тогда apply объекта-компаньона нельзя будет использовать как есть).

1
ответ дан 5 December 2019 в 12:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: