Как получить экземпляр classа типа, связанного с привязкой к контексту?
Примечание. Я задаю этот вопрос, чтобы ответить на него сам, но другие ответы приветствуются.
Рассмотрим следующий простой способ:
def add[T](x: T, y: T)(implicit num: Numeric[T]) = num.plus(x,y)
Я могу переписать это с использованием контекста следующим образом
- Запрос Spark SQL DataFrame со сложными типами
- Как получить последнюю дату определенного месяца с помощью JodaTime?
- Пункты памяти и стиль кодирования по Java VM
- Неявное ключевое слово перед параметром в анонимной функции в Scala
- В Scala, как я могу подclassифицировать class Java с несколькими конструкторами?
def add[T: Numeric](x: T, y: T) = ??.plus(x,y)
но как мне получить экземпляр типа Numeric[T]
чтобы я мог вызвать метод plus
?
- Многострочный литерал функции в качестве аргументов в Scala
- Почему Scala поддерживает теневые переменные?
- Как закрепить два (или более) DataFrame в Spark
- Как найти размер вспышки RDD / Dataframe?
- Как распечатать содержимое RDD?
- Как определить разбиение DataFrame?
- Скорость компиляции Java и скорость компиляции Scala
- Увеличить размер кучи JVM для Scala?
Использование неявного метода
Наиболее распространенным и общим подходом является использование неявного метода , определенного в Predef:
def add[T: Numeric](x: T, y: T) = implicitly[Numeric[T]].plus(x,y)
Очевидно, что это несколько подробное и требует повторения имени типа.
Ссылка на параметр доказательства ( не надо! )
Другой альтернативой является использование имени неявного параметра доказательства, автоматически генерируемого компилятором:
def add[T: Numeric](x: T, y: T) = evidence$1.plus(x,y)
Удивительно, что этот метод является даже законным, и на практике его нельзя использовать, поскольку имя параметра доказательства может измениться.
Контекст более высокого сорта ( введение context
метода )
Вместо этого можно использовать расширенную версию метода implicitly
. Обратите внимание, что неявный метод определяется как
def implicitly[T](implicit e: T): T = e
Этот метод просто полагается на компилятор, чтобы вставить неявный объект правильного типа из окружения в вызов метода, а затем возвращает его. Мы можем сделать немного лучше:
def context[C[_], T](implicit e: C[T]) = e
Это позволяет нам определить наш метод add
как
def add[T: Numeric](x: T, y: T) = context.plus(x,y)
Параметры типа метода context
Numeric
и T
выводятся из области! К сожалению, существуют обстоятельства, при которых этот context
метод не будет работать. Когда параметр типа имеет несколько границ контекста или, например, есть несколько параметров с различными границами контекста. Мы можем решить эту проблему с чуть более сложной версией:
class Context[T] { def apply[C[_]]()(implicit e: C[T]) = e } def context[T] = new Context[T]
Эта версия требует от нас указать параметр типа каждый раз, но обрабатывает несколько параметров типа.
def add[T: Numeric](x: T, y: T) = context[T]().plus(x,y)
По крайней мере, начиная с Scala 2.9 вы можете сделать следующее:
import Numeric.Implicits._ def add[T: Numeric](x: T, y: T) = x + y add(2.8, 0.1) // res1: Double = 2.9 add(1, 2) // res2: Int = 3
Этот ответ описывает другой подход, который приводит к более читабельному самодокументируемому клиентскому коду.
мотивация
Метод context
который я описал ранее, является очень общим решением, которое работает с любым типом classа без каких-либо дополнительных усилий. Однако это может быть нежелательным по двум причинам:
-
Метод
context
не может использоваться, когда параметр типа имеет несколько границ контекста, поскольку компилятор не имеет возможности определить, какая граница контекста предназначена. -
Ссылка на метод общего
context
вредит читабельному коду.
Тип-специфические методы
Использование метода, привязанного к желаемому типу, делает код клиента более читаемым. Это подход, используемый в стандартной библиотеке для classа типа Manifest:
// definition in Predef def manifest[T](implicit m: Manifest[T]) = m // example usage def getErasure[T: Manifest](x: T) = manifest[T].erasure
Обобщение этого подхода
Основной недостаток использования методов, специфичных для типа, заключается в том, что для каждого типа classа должен быть определен дополнительный метод. Мы можем облегчить этот процесс следующими определениями:
class Implicitly[TC[_]] { def apply[T]()(implicit e: TC[T]) = e } object Implicitly { def apply[TC[_]] = new Implicitly[TC] }
Затем может быть определен новый тип неявного стиля типа типа для любого типа:
def numeric = Implicitly[Numeric] // or val numeric = Implicitly[Numeric]
Наконец, клиентский код может использовать Неявно следующим образом:
def add[T: Numeric](x: T, y: T) = numeric[T].plus(x, y)