2017-08-01 61 views
2

有沒有辦法創建一個隱式類來提供一個自定義函數來返回一個與記錄風格的singleton操作符->>相同類型的FieldType?包裝在函數中創建Shapeless FieldType

我想這樣做:

import shapeless.syntax.singleton._ 

implicit class FieldMaker[S <: Symbol](val s: S) { 
    def make[T](t: T) = s ->> t 
} 

,使下面的兩個值具有相同的類型:

val first = 'test ->> Foo("bar") 
val second = 'test make Foo("bar") 

在以前的嘗試中,我不斷收到由宏挫敗mkSingletonOps。任何意見將是有益的!

更新:

這樣做的動機從創建一個DSL,並試圖小心地控制它的語法造成的。上面的簡化示例跳過了這個隱式類在DSL中特定的目的,即將一個函數應用於返回DSL中其他位置所需的類型類。

甲多個示例性的情況將是:

import shapeless.syntax.singleton._ 

def func(t: T): SomeTypeclass[T] = _ // elided 

implicit class FieldMaker[S <: Symbol](val s: S) { 
    def make[T](t: T) = s ->> func(t) 
} 

使得以下兩個值具有相同的類型:

val first = 'test ->> func(Foo("bar")) 
val second = 'test make Foo("bar") 

分配給second表達是用於DSL所需的語法。

+0

爲什麼不使用(或重命名)' - >>'? –

+0

我正在創建一個比上面的例子更多的DSL,並且正在嘗試將(make)的語法制作成特定的內容,所以DSL看起來就像我打算的那樣。使用' - >>'可以工作,但會暴露該操作員和其他工作(上面未顯示)。重命名' - >>'會幫助語法,但仍然需要公開其他工作(歸結爲f(Foo(「bar」))) – Ryan

+0

@MilesSabin這是否意味着在當前版本的無形? – Ryan

回答

1

可能沒有。

import shapeless.Witness 
    import shapeless.labelled.FieldType 
    import shapeless.syntax.singleton._ 

    implicit class FieldMaker[S <: Symbol](val s: S) { 
    def make[T](t: T): FieldType[s.type, T] = s ->>[T] t 
    } 

    case class Foo(s: String) 

    val first: FieldType[Witness.`'test`.T, Foo] = 'test ->>[Foo] Foo("bar") 
    val second: FieldType[fm.s.type, Foo] forSome { val fm: FieldMaker[Symbol] } = 
    'test make[Foo] Foo("bar") 
    val third: FieldType[fm.s.type, Foo] forSome { val fm: FieldMaker[Witness.`'test`.T] } = 
    'test.narrow make[Foo] Foo("bar") 

s ->>[T] t類型是FieldType[s.type, T],這不依賴於類型參數S。 但是s.type中的s是什麼?在隱式轉換之前,它是'test,但在此之後它只是FieldMaker[S](實例,僅在隱式轉換期間存在的實例)的實例的字段,所以我們具有存在類型。只要我們有存在型,我們就不能回到'test

這是如何隱含工作。


其實最後我發現,使同一類型的secondfirst(儘管它使用.narrow的方式。我用類型和隱式轉換替換了隱式轉換。

import shapeless.Witness 
    import shapeless.labelled.FieldType 
    import shapeless.syntax.singleton._ 
    import [email protected]@ 

    trait FieldMaker[S <: Symbol, T] { 
    def make(t: T): FieldType[S, T] 
    } 

    object FieldMaker { 
    implicit def mkFieldMaker[/*S <: Symbol*/U, T](implicit 
                witness: Witness.Aux[/*S*/Symbol @@ U] 
               ): FieldMaker[/*S*/Symbol @@ U, T] = { 
     val name: /*S*/Symbol @@ U = witness.value 
     new FieldMaker[/*S*/Symbol @@ U, T] { 
     override def make(t: T): FieldType[/*S*/Symbol @@ U, T] = 
      (name ->>[T] t).asInstanceOf[FieldType[/*S*/Symbol @@ U, T]] 
     } 
    } 

    object op { 
     implicit class FieldMakerOp[S <: Symbol](s: S) { 
     def make[T](t: T)(implicit fm: FieldMaker[S, T]): FieldType[S, T] = fm.make(t) 
     } 
    } 

    } 

    case class Foo(s: String) 

    val first: FieldType[Witness.`'test`.T, Foo] = 'test ->>[Foo] Foo("bar") 

    import FieldMaker.op._ 
    val second: FieldType[Witness.`'test`.T, Foo] = 'test.narrow make/*[Foo]*/ Foo("bar") 
+0

分配給'second'的表達式的語法不同於例。使用'.narrow'是不同之處,也突出了我在相關嘗試中遇到的問題:Symbol的單例類型的標記是通過無單形的「singleton」對象中的宏完成的。因此,可以在像我這樣的DSL中使用的方法(例如' - >>')的語法僅限於由宏返回的'trait SingletonOps'中存在的內容。我建議你不要忽略你的「可能不」。聲明,我會接受這個答案......感謝'.narrow'示例。 – Ryan

+0

我發現你的[編程與依賴類型在斯卡拉](https://stepik.org/course/2294)課程。它看起來非常有趣,並且似乎相關推薦給發現這個問題/評論的任何人。 – Ryan

+1

@Ryan好吧,我細細地編輯了我的答案。我很高興你對我的課程感興趣。現在我正在研究新的。 –