我如何最好地构造具有默认值的无形记录?
How do I best construct a shapeless record with a default value?
假设我有一个无形的记录:
trait T
case object Ti extends T
case object Ta extends T
case object To extends T
type R = Record.`Ta -> Option[String], Ti -> Option[Int]`.T
val r: R = (Ta ->> Option("plif")) :: (Ti ->> Option(4)) :: HNil
我想编写一个函数 get
这样:
get(Ta) == Some("plif")
get(Ti) == Some(4)
get(To) == None
实现此目标的最佳方法是什么?
一个简单的解决方案是为默认情况提供您自己的 Selector
实例:
class DefaultSelector[R <: HList, K] extends Selector[R, K] {
type Out = Option[Nothing]
def apply(l: R): Out = None
}
def get[K, V](k: K)(
implicit sel: Selector[R, K] = new DefaultSelector[R, K]
): sel.Out = sel(r)
但是使用该代码,Scala 的编译器可能难以为默认情况的结果提供 TypeTag
s。
所以要解决这个问题,您还可以编写一个新的类型类 DefaultSelector
,如果没有找到 Selector
,它将默认为 None: Option[Nothing]
:
import shapeless._
import shapeless.ops.record._
trait DefaultSelector[R <: HList, K] {
type Out
def apply(r: R): Out
}
sealed trait LowPriorityDefaultSelector {
type Aux[R <: HList, K, V] = DefaultSelector[R, K] { type Out = V }
case class Impl[R <: HList, K, V](get: R => V) extends DefaultSelector[R, K] {
type Out = V
def apply(r: R): Out = get(r)
}
implicit def default[R <: HList, K, V](
implicit ev: Option[Nothing] =:= V // tricking Scala's implicit resolution
): Aux[R, K, V] =
Impl[R, K, V](Function.const(None))
}
object DefaultSelector extends LowPriorityDefaultSelector {
implicit def existing[R <: HList, K, V](
implicit sel: Selector.Aux[R, K, V]
): Aux[R, K, V] =
Impl[R, K, V](sel.apply)
}
那么get
函数就变成了:
def get[K, V](k: K)(implicit sel: DefaultSelector[R, K]): sel.Out = sel(r)
结果(对于两种解决方案)是:
scala> get(Ti)
res0: Option[Int] = Some(4)
scala> get(Ta)
res1: Option[String] = Some(plif)
scala> get(To)
res2: Option[Nothing] = None
假设我有一个无形的记录:
trait T
case object Ti extends T
case object Ta extends T
case object To extends T
type R = Record.`Ta -> Option[String], Ti -> Option[Int]`.T
val r: R = (Ta ->> Option("plif")) :: (Ti ->> Option(4)) :: HNil
我想编写一个函数 get
这样:
get(Ta) == Some("plif")
get(Ti) == Some(4)
get(To) == None
实现此目标的最佳方法是什么?
一个简单的解决方案是为默认情况提供您自己的 Selector
实例:
class DefaultSelector[R <: HList, K] extends Selector[R, K] {
type Out = Option[Nothing]
def apply(l: R): Out = None
}
def get[K, V](k: K)(
implicit sel: Selector[R, K] = new DefaultSelector[R, K]
): sel.Out = sel(r)
但是使用该代码,Scala 的编译器可能难以为默认情况的结果提供 TypeTag
s。
所以要解决这个问题,您还可以编写一个新的类型类 DefaultSelector
,如果没有找到 Selector
,它将默认为 None: Option[Nothing]
:
import shapeless._
import shapeless.ops.record._
trait DefaultSelector[R <: HList, K] {
type Out
def apply(r: R): Out
}
sealed trait LowPriorityDefaultSelector {
type Aux[R <: HList, K, V] = DefaultSelector[R, K] { type Out = V }
case class Impl[R <: HList, K, V](get: R => V) extends DefaultSelector[R, K] {
type Out = V
def apply(r: R): Out = get(r)
}
implicit def default[R <: HList, K, V](
implicit ev: Option[Nothing] =:= V // tricking Scala's implicit resolution
): Aux[R, K, V] =
Impl[R, K, V](Function.const(None))
}
object DefaultSelector extends LowPriorityDefaultSelector {
implicit def existing[R <: HList, K, V](
implicit sel: Selector.Aux[R, K, V]
): Aux[R, K, V] =
Impl[R, K, V](sel.apply)
}
那么get
函数就变成了:
def get[K, V](k: K)(implicit sel: DefaultSelector[R, K]): sel.Out = sel(r)
结果(对于两种解决方案)是:
scala> get(Ti)
res0: Option[Int] = Some(4)
scala> get(Ta)
res1: Option[String] = Some(plif)
scala> get(To)
res2: Option[Nothing] = None