具有最小样板的无形 StringLike 特征
Shapeless StringLike trait with minimal boilerplate
我正在尝试定义一种生成类字符串类型的通用方法。例如,我想要
case class Foo1(s: String) extends AnyVal
case class Foo2(s: String) extends AnyVal
...
有scalaz.Show、scalaz.Equal、argonaut.CodecJson等实例。
我知道这可以使用 hacky 方法来实现,例如获取案例 类 生成的 apply/unapply 函数,但我希望提出一个类型安全、无样板的无形状解决方案。这是我想到的最好的:
import scalaz._, Scalaz._
import argonaut._, Argonaut._
import shapeless._
trait HasName[A] {
def to(v: A): String
def fr(v: String): A
}
object HasName {
def apply[A](implicit instance: HasName[A]): HasName[A] = instance
def instance[A](f: A => String, g: String => A): HasName[A] = new HasName[A] { def to(v: A) = f(v); def fr(v: String) = g(v) }
implicit val hlist: HasName[String :: HNil] = new HasName[String :: HNil] {
def to(v: String :: HNil) = v.head
def fr(v: String) = v :: HNil
}
implicit def generic[A, R](implicit F: Generic.Aux[A, R], G: HasName[R]): HasName[A] = instance(
v => G.to(F.to(v)),
v => F.from(G.fr(v))
)
}
trait Name[A] {
val F: HasName[A]
implicit val show: Show[A] = Show.shows(F.to)
implicit val read: Read[A] = Read.readUnsafe(F.fr)
implicit val json: CodecJson[A] = CodecJson[A](v => jString(F.to(v)), c => c.as[String] map F.fr)
implicit val equal: Equal[A] = Equal.equalA[A]
}
那么用户可以做
case class Foo1(s: String) extends AnyVal
object Foo1 extends Name[Foo1] {
val F = cachedImplicit[HasName[Foo1]]
}
这不是太多的样板,但仍然有那个讨厌的 F。我试过这个:
class Name[A](implicit F: HasName[A]) {
implicit val show: Show[A] = Show.shows(F.to)
implicit val read: Read[A] = Read.readUnsafe(F.fr)
implicit val json: CodecJson[A] = CodecJson[A](v => jString(F.to(v)), c => c.as[String] map F.fr)
implicit val equal: Equal[A] = Equal.equalA[A]
}
在呼叫站点哪个更好:
object Foo1 extends Name[Foo1]
但是没用;不能有隐式的按名称参数,也不能传递非按名称的循环引用。
关于如何使调用者和被调用者的代码都保持良好的任何想法?
您可以利用您的 HasName
将隐含在范围内的事实来执行以下操作
trait Name[A] {
implicit def show(implicit F: HasName[A]): Show[A] = Show.shows(F.to)
implicit def read(implicit F: HasName[A]): Read[A] = Read.readUnsafe(F.fr)
implicit def json(implicit F: HasName[A]): CodecJson[A] = CodecJson[A](v => jString(F.to(v)), c => c.as[String] map F.fr)
implicit def equal(implicit F: HasName[A]): Equal[A] = Equal.equalA[A]
}
我正在尝试定义一种生成类字符串类型的通用方法。例如,我想要
case class Foo1(s: String) extends AnyVal
case class Foo2(s: String) extends AnyVal
...
有scalaz.Show、scalaz.Equal、argonaut.CodecJson等实例。 我知道这可以使用 hacky 方法来实现,例如获取案例 类 生成的 apply/unapply 函数,但我希望提出一个类型安全、无样板的无形状解决方案。这是我想到的最好的:
import scalaz._, Scalaz._
import argonaut._, Argonaut._
import shapeless._
trait HasName[A] {
def to(v: A): String
def fr(v: String): A
}
object HasName {
def apply[A](implicit instance: HasName[A]): HasName[A] = instance
def instance[A](f: A => String, g: String => A): HasName[A] = new HasName[A] { def to(v: A) = f(v); def fr(v: String) = g(v) }
implicit val hlist: HasName[String :: HNil] = new HasName[String :: HNil] {
def to(v: String :: HNil) = v.head
def fr(v: String) = v :: HNil
}
implicit def generic[A, R](implicit F: Generic.Aux[A, R], G: HasName[R]): HasName[A] = instance(
v => G.to(F.to(v)),
v => F.from(G.fr(v))
)
}
trait Name[A] {
val F: HasName[A]
implicit val show: Show[A] = Show.shows(F.to)
implicit val read: Read[A] = Read.readUnsafe(F.fr)
implicit val json: CodecJson[A] = CodecJson[A](v => jString(F.to(v)), c => c.as[String] map F.fr)
implicit val equal: Equal[A] = Equal.equalA[A]
}
那么用户可以做
case class Foo1(s: String) extends AnyVal
object Foo1 extends Name[Foo1] {
val F = cachedImplicit[HasName[Foo1]]
}
这不是太多的样板,但仍然有那个讨厌的 F。我试过这个:
class Name[A](implicit F: HasName[A]) {
implicit val show: Show[A] = Show.shows(F.to)
implicit val read: Read[A] = Read.readUnsafe(F.fr)
implicit val json: CodecJson[A] = CodecJson[A](v => jString(F.to(v)), c => c.as[String] map F.fr)
implicit val equal: Equal[A] = Equal.equalA[A]
}
在呼叫站点哪个更好:
object Foo1 extends Name[Foo1]
但是没用;不能有隐式的按名称参数,也不能传递非按名称的循环引用。
关于如何使调用者和被调用者的代码都保持良好的任何想法?
您可以利用您的 HasName
将隐含在范围内的事实来执行以下操作
trait Name[A] {
implicit def show(implicit F: HasName[A]): Show[A] = Show.shows(F.to)
implicit def read(implicit F: HasName[A]): Read[A] = Read.readUnsafe(F.fr)
implicit def json(implicit F: HasName[A]): CodecJson[A] = CodecJson[A](v => jString(F.to(v)), c => c.as[String] map F.fr)
implicit def equal(implicit F: HasName[A]): Equal[A] = Equal.equalA[A]
}