2017-11-17 87 views
2

有沒有人知道如何使用無形的測試工作。使用無形的類型級別過濾

package net.jtownson.swakka.jsonschema 

import org.scalatest.FlatSpec 
import org.scalatest.Matchers._ 

class OptionalFieldSpec extends FlatSpec { 

    case class A(i: Int, j: Option[Int]) 

    "an extractor of some kind" should "get the (non)optional fields from a case class" in { 

    extractNonOptionalFieldNames[A] shouldBe List("i") 

    extractOptionalFieldNames[A] shouldBe List("j") 

    } 

    def extractNonOptionalFieldNames[T <: Product](/* implicit typeclass instances? */): List[String] = ??? 

    def extractOptionalFieldNames[T <: Product]: List[String] = ??? 

} 

我有一個不運行時實例或它的仿製藥,因爲我在對的情況下A類,它是獨立於任何特定實例的創建工作JsonSchema。該模式有一個所需的字段,該字段是非可選字段的列表。例如

{ 
    "type" -> "object", 
    "required" -> ["i"], 
    "properties" -> { 
    "i" -> { 
     "type" -> "integer", 
     "format" -> "int32" 
    } 
    } 
} 
+0

你想'LabelledGeneric' - 這將使你的字段名的類型級證人,以及它們的類型。除此之外,似乎你只是想過濾這個HList。 – Alec

回答

3

事情是這樣的:

trait FieldNameExtractor[T] extends Serializable { 
    import shapeless.ops.hlist.{RightFolder, ToTraversable} 
    import shapeless.ops.record.Keys 
    import shapeless.{HList, HNil, LabelledGeneric, Poly2} 

    /** 
    * Extracts filtered field names for type [[T]], 
    * given a polymorphic function that acts as the type filter 
    */ 
    def extract[L <: HList, R <: HList, O <: HList](op: Poly2)(
     implicit lgen: LabelledGeneric.Aux[T, L], 
     folder: RightFolder.Aux[L, HNil.type, op.type, R], 
     keys: Keys.Aux[R, O], 
     traversable: ToTraversable.Aux[O, List, Symbol] 
): List[String] = { 
    val result = keys().to[List] 
    result.map(_.name) 
    } 
} 

object FieldNameExtractor { 
    def apply[T] = new FieldNameExtractor[T] {} 
} 

用法:

import org.scalatest.FlatSpec 
import org.scalatest.Matchers._ 

class Test extends FlatSpec { 
    /* type filters */ 
    import shapeless.{HList, Poly2} 
    import shapeless.labelled.KeyTag, shapeless.tag.Tagged 

    type FilterO[A, T] = Option[A] with KeyTag[Symbol with Tagged[T], Option[A]] 

    trait Ignore extends Poly2 { 
    implicit def default[A, L <: HList] = at[A, L]((_, l) => l) 
    } 
    trait Accept extends Poly2 { 
    implicit def default[A, L <: HList] = at[A, L](_ :: _) 
    } 

    object allOptions extends Ignore { 
    implicit def option[A, T, L <: HList] = at[FilterO[A, T], L](_ :: _) 
    } 
    object noOptions extends Accept { 
    implicit def option[A, T, L <: HList] = at[FilterO[A, T], L]((_, l) => l) 
    } 

    "an extractor of some kind" should "get the (non)optional fields from a case class" in { 
    case class A(i: Int, j: Option[Int], k: String) 

    val fne = FieldNameExtractor[A] 
    fne.extract(noOptions) shouldBe List("i", "k") // extractNonOptionalFieldNames 
    fne.extract(allOptions) shouldBe List("j")  // extractOptionalFieldNames 
    } 
} 
+0

美麗。謝謝。其實,我認爲你發佈的第一個解決方案也很好。它的代碼少了幾行。 –

+0

我設法使它更加可重用和美觀。所有的「魔術」都發生在「提取」方法中;所以例如'extractOptionalFieldNames'實現只傳遞所需的參數:你自定義類型的值和作爲過濾器的多態函數('Poly' impl) –

+0

已修改,因此不需要'A'的運行時實例 –

2

這裏使用的一種方法類型類:

​​3210

有可能需要幾件事情解釋:

  • 既然您提到您沒有運行時實例A。什麼是您想要回來的類型級別表示?在這個解決方案中,我只是返回了一個HList證人的選擇。我不認爲List[String]表示就足夠了,因爲過濾掉非可選值將具有與無所事事相同的類型。
  • 類型類有一個優先級,所以過濾選項與反向優先級相同。

它可以像這樣使用:

case class A(i: Int, j: Option[Int], k: Option[Long]) 
val x = LabelledGeneric[A] 
type filteredType = OptionExtractor[x.Repr] 
//type B = Symbol with shapeless.tag.Tagged[String("j")] :: Symbol with shapeless.tag.Tagged[String("k")] :: shapeless.HNil 
+0

乾杯,Jamborta。 (我提高了你的解決方案,但我沒有足夠的街道信譽來計算它,當我更高級時,我會回到它的位置:-) –