2 ответа

Лучший ответ

Ого, это старый! Я начну с того, что немного поправлю код и приведу его в соответствие с текущими идиоматическими соглашениями:

case class Flat[T, U](fn: T => List[U]) 

implicit def recFlattenFn[T, U](
  implicit f: Flat[T, U] = Flat((xs: T) => List(xs))
) = Flat((xs: List[T]) => xs flatMap f.fn)

def recFlatten[T, U](xs: List[T3])(implicit f: Flat[List[T], U]) = f fn xs 

Затем, без лишних слов, разбейте код. Во-первых, у нас есть класс Flat:

case class Flat[T, U](fn: T => List[U]) 

Это не что иное, как именованная оболочка для функции T => List[U], функции, которая будет строить List[U] при наличии экземпляра типа T. Обратите внимание, что T здесь также может быть List[U], или U, или List[List[List[U]]] и т. Д. Обычно такая функция может быть напрямую указана как тип параметр. Но мы собираемся использовать его в неявных выражениях, поэтому именованная оболочка избегает любого риска неявного конфликта.

Затем, работая в обратном направлении от recFlatten:

def recFlatten[T, U](xs: List[T])(implicit f: Flat[List[T], U]) = f fn xs 

Этот метод примет xs (List[T]) и преобразует его в U. Для этого он находит неявный экземпляр Flat[T,U] и вызывает вложенную функцию fn

Затем настоящая магия:

implicit def recFlattenFn[T, U](
  implicit f: Flat[T, U] = Flat((xs: T) => List(xs))
) = Flat((xs: List[T]) => xs flatMap f.fn)

Это удовлетворяет неявному параметру, требуемому recFlatten, но также принимает другой неявный параметр. Самое главное:

  • recFlattenFn может действовать как собственный неявный параметр
  • он возвращает Flat [List [X], X], поэтому recFlattenFn будет неявно разрешен как Flat[T,U], только если T является List
  • неявное f может вернуться к значению по умолчанию, если неявное разрешение не удается (т. е. T НЕ является List)

Возможно, лучше всего это понять в контексте одного из примеров:

recFlatten(List(List(1, 2, 3), List(4, 5))) 
  • Тип T выводится как List[List[Int]]
  • неявный поиск выполняется для `Flat [List [List [Int]], U]
  • этому соответствует рекурсивно определенный recFlattenFn

Говоря в широком смысле:

recFlattenFn[List[List[Int]], U] ( f =
  recFlattenFn[List[Int], U] ( f =
    Flat[Int,U]((xs: T) => List(xs)) //default value
  )
)

Обратите внимание, что recFlattenFn будет соответствовать только неявному поиску Flat[List[X], X], а параметры типа [Int,_] не соответствуют этому совпадению, потому что Int не является List. Это то, что вызывает возврат к значению по умолчанию.

Вывод типа также работает в обратном направлении вверх по этой структуре, разрешая параметр U на каждом уровне рекурсии:

recFlattenFn[List[List[Int]], Int] ( f =
  recFlattenFn[List[Int], Int] ( f =
    Flat[Int,Int]((xs: T) => List(xs)) //default value
  )
)

Это просто вложение экземпляров Flat, каждый из которых (кроме самого внутреннего) выполняет операцию flatMap для развертывания одного уровня вложенной структуры List. Самый внутренний Flat просто объединяет все отдельные элементы в один List.

Что и требовалось доказать

12
Kevin Wright 28 Авг 2011 в 07:36

Хорошим решением может быть попытка посмотреть, как вводятся типы. Чтобы избежать двусмысленности, переименуем дженерики:

case class Flat[T, U](fn : T => List[U]) 

implicit def recFlattenFn[T2, U2](implicit f : Flat[T2, U2] = 
                                    Flat((l : T2) => List(l))) = 
  Flat((l : List[T2]) => l.flatMap(f.fn)) 

def recFlatten[T3, U3](l : List[T3])(implicit f : Flat[List[T3], U3]) = f.fn(l) 

В первом случае, res0, типом T3 является Int, вы пока не можете сделать вывод о типе U3, но вы знаете, что вам понадобится Flat[List[Int, U3]] объект, который будет предоставлен неявно. Есть только один «неявный кандидат»: результат функции recFlattenFn и его тип Flat[List[T2], List[U2]]. Таким образом, T2 = Int и U2 = U3 (что нам еще нужно вывести).

Теперь, если мы хотим иметь возможность использовать recFlatten, мы должны предоставить ему параметр f. Вот уловка . Вы можете использовать неявный тип Flat[Int, U2] или значение по умолчанию типа Int => List[Int]. Давайте посмотрим на возможные имплициты. Как объяснялось ранее, recFlattenFn может предоставить объект Flat[List[T2], U2] (для новых T2 и U2) объекта. На данный момент это не соответствует ожидаемой сигнатуре f. Таким образом, никакие неявные варианты здесь не подходят, и мы должны использовать аргумент по умолчанию. Поскольку тип аргумента по умолчанию - Int => List [Int], U2 и U3 равны Int, и мы идем.

Надеюсь, эта длинная проза поможет. Я оставляю вас с разрешением res1 и res2.

2
Nicolas 27 Авг 2011 в 08:40