Безжизненное: карта от копродукта до другого копродукта
В следующем случае я пытаюсь сделать полиморфную функцию для преобразования RawFeatureValue
в 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
}
Однако результирующий тип 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] ^
Как сказать бесформенным, что два Int
следует рассматривать как один?
Ответы
Ответ 1
Самый простой способ сделать это, о котором я могу думать, состоял бы в том, чтобы сопоставить каждый элемент в вашем целевом сопрограмме, а затем объединить результат:
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))
}
Это работает как ожидалось:
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
Это решение неплохое, но похоже, что он может быть достаточно полезен для одной операции.
Ответ 2
В качестве альтернативы, методы на Coproduct
позволяют сделать это без Poly
(если это позволяет ваш реальный вариант использования) - и меньше шаблона. Пусть определим
def refine(v: RawFeatureValue): RefinedFeatureValue =
v.removeElem[String]
.left.map(s => Coproduct[RefinedFeatureValue](s.hashCode))
.merge
Затем вы можете сделать
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
Это состоит из:
- разбиение
RawFeatureValue
на String
, с одной стороны, и остальные элементы на другом (которые делают RefinedFeatureValue
), с removeElem
, возвращая a Either[String, RefinedFeatureValue]
,
- отображение слева от результата, чтобы преобразовать его и упаковать в
RefinedFeatureValue
,
- и объединить полученный
Either[RefinedFeatureValue, RefinedFeatureValue]
в один RefinedFeatureValue
.
Ответ 3
В качестве альтернативы, если вы не настроены на полиморфную функцию, вы можете использовать класс типа преобразования в этой библиотеке: https://github.com/xdotai/typeless/
Вот класс конвертируемого типа, который преобразует один Coproduct в вариант другого Coproduct: https://github.com/xdotai/typeless/blob/master/src/main/scala/coproduct/convert.scala
Добавить в зависимости:
libraryDependencies += "ai.x" %% "typeless" % "0.2.5"
код:
scala> import ai.x.typeless.coproduct.Convert.Ops
import ai.x.typeless.coproduct.Convert.Ops
scala> import shapeless._
import shapeless._
scala> type A = String :+: Double :+: CNil
defined type alias A
scala> type B = Double :+: String :+: List[Int] :+: CNil
defined type alias B
scala> Coproduct[A]("test").convert[B]
res0: Option[B] = Some(Inr(Inl(test)))