2017-06-01 71 views
3

我想定義一個製作字符串類型的一般方法。例如,我想Shapeless StringLike特徵與最小樣板

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] 

但它不起作用;您不能擁有隱含的名稱參數,並且您無法傳遞非名稱的循環引用。

關於如何保持調用者和被調用者代碼的任何想法都很好?

回答

1

你可以使用一個事實,即你的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] 
} 
+0

好主意。謝謝! – seanmcl