在 Scala 中对 case class 应用递归操作
Applying recursive operation on a case class in Scala
我正在尝试找到一种优雅的方式来执行以下操作。
假设我们有以下 类:
case class Foo(fooName: String, bar: Bar, baz: Baz)
case class Bar(barName: String, baz: Baz)
case class Baz(bazName: String)
我希望能够更改名称(在本例中为 Foo 及其 children objects 以某种方式),例如,在它们前面加上一些标题。
我可能会定义一个用于此目的的类型类:
trait Titled[T] {
def titled(t: T)(title: String): T
}
对于 Foo,实现会这样做:
implicit val fooTitled: Titled[Foo] = new Titled[Foo] {
def titled(foo: Foo)(title: String): Foo = foo.copy(fooName = title + fooName)
}
可能还有其他实现,例如 Esquired,它会在名称后缀 "Esq."
但这不会更新我想要的 children 值 bar 和 baz 的标题。
我最终想要的结果是这样的:
//import all the implicits needed
val foo = Foo(...)
val titledFoo = foo.title("Mr. ") // names are prefixed with Mr.
val andEsquired = foo.esquire // names are also suffixed with Esq
现在,我一直在网上寻找是否有可能做到这一点,并且我已经看到了 shapeless 库,但我仍然没有达到可以破译和创建这样的 Scala 知识的水平 "magic" 如果可能的话。
我不一定要寻找一个完整的解决方案(尽管我会很感激),但如果有人能给我指出正确的方向,给我一些例子、教程或类似的东西,我会非常感谢。
尝试以下类型 class。我假设所有应该具有类型 class 实例的 classes 都具有类型为 String
的第一个字段作为前缀。
import shapeless.ops.hlist.IsHCons
import shapeless.{::, Generic, HList, HNil}
trait Titled[T] {
def titled(t: T)(title: String): T
}
object Titled {
//implicit def identity[A]: Titled[A] = new Titled[A] {
// override def titled(t: A)(title: String): A = t
//}
implicit def transform[A <: Product, L <: HList, H, T <: HList](implicit
generic: Generic.Aux[A, L],
isHCons: IsHCons.Aux[L, String, T],
tailTitled: HListTitled[T]): Titled[A] = new Titled[A] {
override def titled(t: A)(title: String): A = {
val l = generic.to(t)
val head = isHCons.head(l)
val tail = isHCons.tail(l)
val newHead = title + head
val newTail = tailTitled.titled(tail)(title)
val newL = isHCons.cons(newHead, newTail)
generic.from(newL)
}
}
}
trait HListTitled[L <: HList] {
def titled(t: L)(title: String): L
}
object HListTitled {
implicit val hnil: HListTitled[HNil] = new HListTitled[HNil] {
override def titled(t: HNil)(title: String): HNil = HNil
}
implicit def hcons[H, T <: HList](implicit
headTitled: Titled[H],
tailTitled: HListTitled[T]): HListTitled[H :: T] = new HListTitled[H :: T] {
override def titled(t: H :: T)(title: String): H :: T =
headTitled.titled(t.head)(title) :: tailTitled.titled(t.tail)(title)
}
}
implicit class TitledOps[T](t: T) {
def titled(title: String)(implicit ttld: Titled[T]): T = ttld.titled(t)(title)
}
case class Foo(fooName: String, bar: Bar, baz: Baz)
case class Bar(barName: String, baz: Baz)
case class Baz(bazName: String)
case class Foo1(fooName: String, bar: Bar, baz: Baz, i: Int)
Foo("Johnson", Bar("Smith", Baz("Doe")), Baz("James")).titled("Mr. ")
// Foo(Mr. Johnson,Bar(Mr. Smith,Baz(Mr. Doe)),Baz(Mr. James))
Foo1("Johnson", Bar("Smith", Baz("Doe")), Baz("James"), 10).titled("Mr. ")
// doesn't compile
// if you want it to compile uncomment "identity"
我正在尝试找到一种优雅的方式来执行以下操作。
假设我们有以下 类:
case class Foo(fooName: String, bar: Bar, baz: Baz)
case class Bar(barName: String, baz: Baz)
case class Baz(bazName: String)
我希望能够更改名称(在本例中为 Foo 及其 children objects 以某种方式),例如,在它们前面加上一些标题。
我可能会定义一个用于此目的的类型类:
trait Titled[T] {
def titled(t: T)(title: String): T
}
对于 Foo,实现会这样做:
implicit val fooTitled: Titled[Foo] = new Titled[Foo] {
def titled(foo: Foo)(title: String): Foo = foo.copy(fooName = title + fooName)
}
可能还有其他实现,例如 Esquired,它会在名称后缀 "Esq."
但这不会更新我想要的 children 值 bar 和 baz 的标题。
我最终想要的结果是这样的:
//import all the implicits needed
val foo = Foo(...)
val titledFoo = foo.title("Mr. ") // names are prefixed with Mr.
val andEsquired = foo.esquire // names are also suffixed with Esq
现在,我一直在网上寻找是否有可能做到这一点,并且我已经看到了 shapeless 库,但我仍然没有达到可以破译和创建这样的 Scala 知识的水平 "magic" 如果可能的话。
我不一定要寻找一个完整的解决方案(尽管我会很感激),但如果有人能给我指出正确的方向,给我一些例子、教程或类似的东西,我会非常感谢。
尝试以下类型 class。我假设所有应该具有类型 class 实例的 classes 都具有类型为 String
的第一个字段作为前缀。
import shapeless.ops.hlist.IsHCons
import shapeless.{::, Generic, HList, HNil}
trait Titled[T] {
def titled(t: T)(title: String): T
}
object Titled {
//implicit def identity[A]: Titled[A] = new Titled[A] {
// override def titled(t: A)(title: String): A = t
//}
implicit def transform[A <: Product, L <: HList, H, T <: HList](implicit
generic: Generic.Aux[A, L],
isHCons: IsHCons.Aux[L, String, T],
tailTitled: HListTitled[T]): Titled[A] = new Titled[A] {
override def titled(t: A)(title: String): A = {
val l = generic.to(t)
val head = isHCons.head(l)
val tail = isHCons.tail(l)
val newHead = title + head
val newTail = tailTitled.titled(tail)(title)
val newL = isHCons.cons(newHead, newTail)
generic.from(newL)
}
}
}
trait HListTitled[L <: HList] {
def titled(t: L)(title: String): L
}
object HListTitled {
implicit val hnil: HListTitled[HNil] = new HListTitled[HNil] {
override def titled(t: HNil)(title: String): HNil = HNil
}
implicit def hcons[H, T <: HList](implicit
headTitled: Titled[H],
tailTitled: HListTitled[T]): HListTitled[H :: T] = new HListTitled[H :: T] {
override def titled(t: H :: T)(title: String): H :: T =
headTitled.titled(t.head)(title) :: tailTitled.titled(t.tail)(title)
}
}
implicit class TitledOps[T](t: T) {
def titled(title: String)(implicit ttld: Titled[T]): T = ttld.titled(t)(title)
}
case class Foo(fooName: String, bar: Bar, baz: Baz)
case class Bar(barName: String, baz: Baz)
case class Baz(bazName: String)
case class Foo1(fooName: String, bar: Bar, baz: Baz, i: Int)
Foo("Johnson", Bar("Smith", Baz("Doe")), Baz("James")).titled("Mr. ")
// Foo(Mr. Johnson,Bar(Mr. Smith,Baz(Mr. Doe)),Baz(Mr. James))
Foo1("Johnson", Bar("Smith", Baz("Doe")), Baz("James"), 10).titled("Mr. ")
// doesn't compile
// if you want it to compile uncomment "identity"