Поскольку в Scala есть явные типы функций, я бы сказал, что если вам нужно передать функцию в вашу функцию, используйте тип функции, т. Е. Ваш вариант B. Для этого они явно существуют.
Не совсем понятно, что вы имеете в виду, говоря, что вы "никогда не видели этого раньше в scala", кстати. Многие стандартные библиотечные методы принимают функции в качестве своих параметров, например, методы преобразования коллекций. Создание псевдонимов типов для передачи семантики более сложного типа также совершенно идиоматично в Scala, и на самом деле не имеет значения, является ли псевдоним типа функцией или нет. Например, один из основных типов в моем текущем проекте на самом деле является псевдонимом типа для функции, которая передает семантику с использованием описательного имени, а также позволяет легко передавать соответствующие функции без необходимости явного создания подкласса некоторой черты.
Мне также важно понять, что синтаксический сахар метода apply
не имеет никакого отношения к тому, как используются функции или функциональные черты. Действительно, apply
методы имеют возможность вызываться только с круглыми скобками; однако это не означает, что различные типы, имеющие apply
метод, даже с одной и той же сигнатурой, являются интероперабельными , и я думаю, что эта совместимость имеет значение в этой ситуации, а также способность легко конструировать экземпляры таких типов. В конце концов, в вашем конкретном примере для вашего кода имеет значение только то, можете ли вы использовать синтаксический сахар на IntMath
или нет, но для пользователей вашего кода возможность легко создать экземпляр IntMath
, а также способность передавать некоторые существующие вещи, которые у них уже есть, как IntMath
, гораздо важнее.
С типами FunctionN
вы можете использовать синтаксис анонимной функции для создания экземпляров этих типов (на самом деле, несколько синтаксисов, по крайней мере, таких: x => y
, { x => y }
, _.x
, method _
, method(_)
). До Scala 2.11 даже не было возможности создавать экземпляры типов «Single Abstract Method», и даже там требуется флаг компилятора для фактического включения этой функции. Это означает, что пользователи вашего типа должны будут написать либо:
SomeMath(10, _ + 1)
или это:
SomeMath(10, new IntMath {
def apply(x: Int): Int = x + 1
})
Естественно, первый подход гораздо яснее.
Кроме того, типы FunctionN
предоставляют единый общий знаменатель функциональных типов, что улучшает взаимодействие. Например, функции в математике не имеют какого-либо «типа», кроме их сигнатуры, и вы можете использовать любую функцию вместо любой другой функции, если их сигнатуры совпадают. Это свойство также полезно в программировании, поскольку позволяет повторно использовать определения, которые у вас уже есть, для различных целей, сокращая количество конверсий и, следовательно, возможные ошибки, которые вы можете совершать при написании этих конверсий.
Наконец, действительно есть ситуации, когда вы захотите создать отдельный тип для вашего функционально-подобного интерфейса. Одним из примеров является то, что с полноценными типами у вас есть возможность определить сопутствующий объект, а сопутствующие объекты участвуют в разрешении имплицитов. Это означает, что если предполагается, что экземпляры вашего типа функции должны предоставляться неявно, то вы можете воспользоваться наличием сопутствующего объекта и определить там некоторые общие последствия, что сделало бы их доступными для всех пользователей типа без дополнительного импорта:
// You might even want to extend the function type
// to improve interoperability
trait Converter[S, T] extends (S => T) {
def apply(source: S): T
}
object Converter {
implicit val intToStringConverter: Converter[Int, String] = new Converter[Int, String] {
def apply(source: Int): String = source.toString
}
}
Здесь полезно иметь фрагмент неявной области видимости, связанный с типом, потому что в противном случае пользователям Converter
потребуется всегда импортировать содержимое некоторого объекта / пакета, чтобы получить неявные определения по умолчанию; однако при таком подходе все значения, определенные в object Converter
, будут найдены по умолчанию.
Однако такие ситуации не очень распространены. Как правило, я думаю, вы должны сначала попытаться использовать обычный тип функции. Если вы обнаружите, что вам нужно нужен отдельный тип, по практическим причинам, только тогда вы должны создать свой собственный тип.