Trait, FunctionN или trait-наследуя-FunctionN в Scala? - PullRequest
8 голосов
/ 18 июня 2010

У меня есть черта в Scala, которая имеет единственный метод.Назовите его Computable, и единственный метод - compute (input: Int): Int.Я не могу понять, должен ли я

  • Оставить его как отдельную черту с помощью одного метода.
  • Наследовать от (Int => Int) и переименовать "compute" в "apply. "
  • Просто избавьтесь от Computable и используйте (Int => Int).

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

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

Как другие люди подходят к этомутип проблемы?Здесь нет правильных или неправильных ответов;Я просто ищу совет.

Ответы [ 4 ]

8 голосов
/ 18 июня 2010

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

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
3 голосов
/ 21 июня 2010

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

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

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

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

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

1 голос
/ 18 июня 2010

Одним из вариантов является определение типа (вы все еще можете назвать его 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 будет почти таким же простым, как написание простых анонимных функций, но у вас все равно будет проверка типов (которую вы потеряете при неявном преобразовании).Кроме того, вы можете смешивать черту без проблем (но тогда применение объекта-компаньона не может быть использовано как есть).

1 голос
/ 18 июня 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...