2017-08-01 30 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好吧,我细细地编辑了我的答案。我很高兴你对我的课程感兴趣。现在我正在研究新的。 –