无形:从副产品到不同副产品的地图

Uta*_*aal 11 scala unions shapeless

在下面,我试图创建一个多态函数将a转换RawFeatureValue为a RefinedFeatureValue.

import shapeless._

object test {
  type RawFeatureValue = Int :+: Double :+: String :+: CNil
  type RefinedFeatureValue = Int :+: Double :+: CNil

  private object convert extends Poly1 {
    implicit def caseInt = at[Int](i => i)
    implicit def caseDouble = at[Double](d => d)
    implicit def caseString = at[String](s => s.hashCode)
  }

  val a = Coproduct[RawFeatureValue](12)
  val b: RefinedFeatureValue = a map convert
}
Run Code Online (Sandbox Code Playgroud)

但是,结果类型Int :+: Double :+: Int :+: CNil与之不兼容RefinedFeatureValue.

[error]  found   : shapeless.:+:[Int,shapeless.:+:[Double,shapeless.:+:[Int,shapeless.CNil]]]
[error]  required: test.RefinedFeatureValue
[error]     (which expands to)  shapeless.:+:[Int,shapeless.:+:[Double,shapeless.CNil]]
[error]   val b: RefinedFeatureValue = a map convert
[error]                                  ^
Run Code Online (Sandbox Code Playgroud)

如何判断两者Int应该被视为一个?

Tra*_*own 11

我能想到的最直接的方法是将每个元素映射到目标coproduct,然后统一结果:

import shapeless._

type RawFeatureValue = Int :+: Double :+: String :+: CNil
type RefinedFeatureValue = Int :+: Double :+: CNil

object convert extends Poly1 {
  implicit val caseInt = at[Int](Coproduct[RefinedFeatureValue](_))
  implicit val caseDouble = at[Double](Coproduct[RefinedFeatureValue](_))
  implicit val caseString = at[String](s =>
    Coproduct[RefinedFeatureValue](s.hashCode))
}
Run Code Online (Sandbox Code Playgroud)

这按预期工作:

scala> val a = Coproduct[RawFeatureValue](12)
a: RawFeatureValue = 12

scala> val b: RefinedFeatureValue = a.map(convert).unify
b: RefinedFeatureValue = 12

scala> val c = Coproduct[RawFeatureValue]("foo")
c: RawFeatureValue = foo

scala> val d: RefinedFeatureValue = c.map(convert).unify
d: RefinedFeatureValue = 101574
Run Code Online (Sandbox Code Playgroud)

这个解决方案并不错,但看起来它似乎足以成为一个单独的操作.


Ale*_*ult 6

或者,Coproducts 上的方法允许在没有Poly(如果你的真实用例允许的话)的情况下这样做 - 并且更少的样板.我们来定义

def refine(v: RawFeatureValue): RefinedFeatureValue =
  v.removeElem[String]
    .left.map(s => Coproduct[RefinedFeatureValue](s.hashCode))
    .merge
Run Code Online (Sandbox Code Playgroud)

那你可以做

scala> val a = Coproduct[RawFeatureValue](12)
a: RawFeatureValue = 12

scala> refine(a)
res1: RefinedFeatureValue = 12

scala> val c = Coproduct[RawFeatureValue]("foo")
c: RawFeatureValue = foo

scala> refine(c)
res2: RefinedFeatureValue = 101574
Run Code Online (Sandbox Code Playgroud)

这包括:

  • 一方面分裂RawFeatureValue成一个String,另一方面分成另一个元素(制作一个RefinedFeatureValue)removeElem,然后返回Either[String, RefinedFeatureValue],
  • 映射在结果的左侧,将其转换并打包成a RefinedFeatureValue,
  • 并将结果合并Either[RefinedFeatureValue, RefinedFeatureValue]为一个RefinedFeatureValue.