从案例类中提取给定类型的值

Noe*_*l M 4 scala shapeless

是否可以使用Shapeless从案例类中提取特定类型的值?到目前为止,我可以这样做:

def fromCaseClass[T, R <: HList](value: T)(implicit ga: Generic.Aux[T, R]): R = {
  ga.to(value)
}
Run Code Online (Sandbox Code Playgroud)

然后,这允许我在程序上提取值:

scala> case class ServiceConfig(host: String, port: Int, secure: Boolean)
defined class ServiceConfig

scala> val instance = ServiceConfig("host", 80, true)
instance: ServiceConfig = ServiceConfig(host,80,true)

scala> fromCaseClass(instance).select[Boolean]
res10: Boolean = true

scala> fromCaseClass(instance).select[Int]
res11: Int = 80
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试编写一个函数来执行此操作时,我会遇到未找到的implicits:

def getByType[C, X](value: C)(implicit ga: Generic.Aux[C, X]): X = {
  fromCaseClass(value).select[X]
}

<console>:12: error: could not find implicit value for parameter ga: shapeless.Generic.Aux[C,R]
       fromCaseClass(value).select[X]
Run Code Online (Sandbox Code Playgroud)

据推测我得到的是因为编译器无法验证我的参数不是case类.有没有办法让我这样做?

我对Shapeless很陌生,所以我不能完全确定我是在尝试做一些疯狂的事情,还是想错过一些简单的事情.

更新

我觉得我离得更近了.我可以这样实现:

def newGetByType[C, H <: HList, R]
     (value: C)
     (implicit ga: Generic.Aux[C, H], selector: Selector[H, R]): R = {
  ga.to(value).select[R]
}
Run Code Online (Sandbox Code Playgroud)

这允许我从案例类中进行选择:

scala> val s: String = newGetByType(instance)
s: String = host
Run Code Online (Sandbox Code Playgroud)

但这似乎只适用于case类中的第一个类型:

scala> val i: Int = newGetByType(instance)
<console>:17: error: type mismatch;
 found   : String
 required: Int
       val i: Int = newGetByType(instance)
Run Code Online (Sandbox Code Playgroud)

我是在正确的轨道上吗?

Mil*_*bin 8

你越来越近了......

您的主要问题newGetByType是它没有给您任何方式来明确指定您希望提取的类型(并且,正如您所观察到的,在LHS上键入val不足以允许它推断).

为什么不能明确指定要提取的类型?这是定义,

def getByType[S, C, L <: HList](value: C)
  (implicit gen: Generic.Aux[C, L], sel: Selector[L, S]): S =
    gen.to(value).select[S]
Run Code Online (Sandbox Code Playgroud)

理想情况下,我们希望能够指定类型参数S,允许Cvalue参数推断,并L通过隐式解析来计算C.不幸的是,Scala不允许我们部分指定类型参数......它是全有或全无.

因此,使其工作的技巧是将类型参数列表拆分为两个:一个可以完全明确指定,一个可以完全推断:这是一种通用技术,而不是一个特定于无形的技术.

我们通过将计算的主要部分移动到辅助类来完成此操作,该辅助类由我们将明确提供的类型进行参数化,

class GetByType[S] {
  def apply[C, L <: HList](value: C)
    (implicit gen: Generic.Aux[C, L], sel: Selector[L, S]): S =
      gen.to(value).select[S]
}
Run Code Online (Sandbox Code Playgroud)

请注意,我们现在可以假设这S是已知的,并且apply可以推断出两个类型参数.我们完成了现在微不足道的定义getByType,它只提供了一个显式类型参数可以去的地方,并实例化了辅助类,

def getByType[S] = new GetByType[S]
Run Code Online (Sandbox Code Playgroud)

这给你你想要的结果,

scala> import shapeless._, ops.hlist._
import ops.hlist._

scala> :paste
// Entering paste mode (ctrl-D to finish)

class GetByType[S] {
  def apply[C, L <: HList](value: C)
    (implicit gen: Generic.Aux[C, L], sel: Selector[L, S]): S =
      gen.to(value).select[S]
}

// Exiting paste mode, now interpreting.

defined class GetByType

scala> def getByType[S] = new GetByType[S]
getByType: [S]=> GetByType[S]

scala> case class ServiceConfig(host: String, port: Int, secure: Boolean)
defined class ServiceConfig

scala> val instance = ServiceConfig("host", 80, true)
instance: ServiceConfig = ServiceConfig(host,80,true)

scala> getByType[String](instance)
res0: String = host

scala> getByType[Int](instance)
res1: Int = 80

scala> getByType[Boolean](instance)
res2: Boolean = true
Run Code Online (Sandbox Code Playgroud)