Есть ли способ получить имплициты для каждого члена HList из Scala Shapeless?

Я пробовал следующее:

type Params = String :: Int :: HNil

implicit val params: Params = "hello" :: 5 :: HNil

// Supposed to create an implicit for string and int if needed
implicit def meberImplicit[A](
  implicit 
  params: Params,
  selector: Selector[Params, A]
): A = params.select[A]

// Summoning a string
implicitly[String]    // compile-time error

Однако я получаю расходящуюся неявную ошибку:

diverging implicit expansion for type String

Я что-то упустил? И, может быть, уже есть встроенный или лучший способ добиться этого?


person Melvic Ybanez    schedule 04.04.2020    source источник
comment
stackoverflow.com/questions/58779756/   -  person Dmytro Mitin    schedule 05.04.2020


Ответы (1)


Проблема в том, что вы слишком универсальны:

implicit def memberImplicit[A](
  implicit // what you put here is irrelevant
): A = ...

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

Но давайте спросим, ​​почему компилятор не может доказать, что вы просто не можете предоставить имплициты, которые вы передаете в memberImplicit для плохих случаев, и поэтому он не будет считать это жизнеспособной альтернативой, и чтобы он мог доказать, что эта ветвь происхождения должна быть вырезать (там, где вы этого не намеревались), двусмысленность устранена, затем торт.

Дело в том, что вы возвращаете тип A. Это означает, что даже если вы добавили туда какое-то ограничение, например, A =:!= Params - в то время как обычно это работает ... вы просто предоставили все эти имплициты, поэтому ограничения типов перестали работать, и внезапно возникновение таких вещей, как, например, Selector[Params, String] имеют более одного способа создания экземпляров. В этой ситуации практически любая реализация, которую вы попробуете, - пока она возвращает A - потерпит неудачу.

Чтобы что-то работало, вы ДОЛЖНЫ ограничить вывод таким образом, чтобы он не совпадал со всем - на самом деле, чем меньше совпадений, тем лучше. Например, создайте отдельный тип-класс для извлечения значений из HLists:

trait Extractable[A] { def extract(): A }
object Extractable {
  implicit def extractHList[H <: HList, A](
    implicit
    h: H,
    selector: Selector[H, A]
  ): Extractable[A] = () => selector(h)
}

def extract[A](implicit extractable: Extractable[A]): A = extractable.extract()

а потом

extract[String] // "hello"
person Mateusz Kubuszok    schedule 04.04.2020
comment
Попробуйте сейчас, я случайно оставил там одну строчку из более старой попытки - person Mateusz Kubuszok; 05.04.2020
comment
Понятно. Я всегда подозревал, что это как-то связано с возвращаемым типом. Спасибо. - person Melvic Ybanez; 05.04.2020