сопоставить HList с default

#scala #shapeless

#scala #бесформенный

Вопрос:

http://scastie.org/22713

Локальная копия:

 /***
scalaVersion := "2.11.8"
addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.7.1")
libraryDependencies   = {
  val shapelessVersion = "2.2.5"
  Seq(
    "com.chuusai" %% "shapeless" % shapelessVersion
  )
}
*/

import shapeless._

case class Foo()

trait Wrapper

case class S(s: String) extends Wrapper
case class I(i: Int) extends Wrapper

object Main extends App {
  object wrap extends Poly1 {
    implicit def caseString = at[String](S.apply)
    implicit def caseInt = at[Int](I.apply)
    implicit def caseOther[T] = at[T](identity)
  }

  type A = Foo :: String :: HNil
  type B = Foo :: Int :: HNil
  type Out = Foo :: Wrapper :: HNil

  val a: A = Foo() :: "foo" :: HNil
  val b: B = Foo() :: 42 :: HNil

  val aw: Out = a.map(wrap)
  val bw: Out = b.map(wrap)

}
  

Ошибки:

 [error] /tmp/renderercqsCBmArxo/src/main/scala/test.scala:56: could not find implicit value for parameter mapper: shapeless.ops.hlist.Mapper[Main.wrap.type,Main.A]
[error]   val aw: Out = a.map(wrap)
[error]                      ^
[error] /tmp/renderercqsCBmArxo/src/main/scala/test.scala:57: could not find implicit value for parameter mapper: shapeless.ops.hlist.Mapper[Main.wrap.type,Main.B]
[error]   val bw: Out = b.map(wrap)
[error]                      ^
[error] two errors found
[error] (compile:compileIncremental) Compilation failed
  

Как бы мне изменить этот последний элемент на другой?

Ответ №1:

Shapeless Poly — это просто способ объединения некоторых неявных экземпляров, которые описывают, что должно происходить с разными типами, поэтому вы можете использовать те же неявные приемы определения приоритетов, которые вы использовали бы в других ситуациях в Scala:

 object wrap extends LowPriorityWrapCases {
  implicit val caseString = at[String](S.apply)
  implicit val caseInt = at[Int](I.apply)
}

trait LowPriorityWrapCases extends Poly1 {
  implicit def caseOther[T] = at[T](identity)
}
  

Это приведет к неявному поиску, сначала проверяющему конкретные случаи, и только затем переходящему к варианту по умолчанию, вместо того, чтобы просто разводить руками из-за двусмысленности, если в hlist есть String Int элемент or .

В качестве примечания я бы предложил предоставить здесь явные аннотации типов:

 object wrap extends LowPriorityWrapCases {
  implicit val caseString: Case.Aux[String, S] = at[String](S.apply)
  implicit val caseInt: Case.Aux[Int, I] = at[Int](I.apply)
}

trait LowPriorityWrapCases extends Poly1 {
  implicit def caseOther[T]: Case.Aux[T, T] = at[T](identity)
}
  

Это немного более шумно, но может избавить вас от некоторых раздражающих и сложных для отладки проблем.