Как получить экземпляр classа типа, связанного с привязкой к контексту?

Примечание. Я задаю этот вопрос, чтобы ответить на него сам, но другие ответы приветствуются.

Рассмотрим следующий простой способ:

def add[T](x: T, y: T)(implicit num: Numeric[T]) = num.plus(x,y) 

Я могу переписать это с использованием контекста следующим образом

 def add[T: Numeric](x: T, y: T) = ??.plus(x,y) 

но как мне получить экземпляр типа Numeric[T] чтобы я мог вызвать метод plus ?

Использование неявного метода

Наиболее распространенным и общим подходом является использование неявного метода , определенного в 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) 
  • Функциональное программирование, карта Scala и свернуть влево
  • Не найден параметр конфигурации для ключа 'akka.version'
  • Как подавить информацию и сообщения о успехе в sbt?
  • Лучший способ конвертировать поле строки в метку времени в Spark
  • «Ошибка: тип несоответствия» в Spark с теми же найденными и требуемыми типами данных
  • Два способа определения функций в Scala. В чем разница?
  • Scala эквивалент Java java.lang.Class Объект
  • Spark spark-submit - jars arguments хочет список запятой, как объявить каталог банок?
  • Что делает ленивый вал?
  • Почему «избегать перегрузки метода»?
  • Создание файла jar из файла Scala
  • Interesting Posts
    Давайте будем гением компьютера.