Fundep实现带有隐含的类型信息

时间:2015-03-18 21:02:23

标签: scala macros scala-macros shapeless

我正在尝试使用fundep实现来增加编译时的安全性。宏的返回类型取决于传递给宏的字符串(即隐含在Foo.scala中)。

我的最终目标是看起来像这样的DSL,

"Eggs, Bacon".shoppingList.order

使用HList命令具有隐式参数的方法,该HList由宏中的字符串“Eggs,Bacon”生成。

我看到了一些奇怪的行为,

  1. 显式调用宏意味着我无法在内联返回的对象上调用该方法
  2. 如果我依赖于使用fundep方法的隐式转换,则类型将被删除,而且我似乎在没有显式转换的情况下转换为类型
  3. 明确实现,然后调用方法就可以了。
  4. Foo.scala有简化的宏,FooExample.scala有奇怪的行为。

    有没有这样做?

    Foo.scala

    import shapeless._
    import scala.language.experimental.macros
    import scala.language.implicitConversions
    import scala.reflect.macros.whitebox
    
    class Foo[L <:  HList](val list: L) { self =>
      def foo: Foo[L] = self
    }
    
    object Foo {
      implicit def materializeFoo[L <: HList](foo: String): Foo[L] = macro FooMacro.materialize_foo_impl[L]
    }
    
    object FooMacro {
      def materialize_foo_impl[L <:  HList : c.WeakTypeTag](c: whitebox.Context)(foo: c.Expr[String]): c.Tree = {
        import c.universe._
        q"""{ new Foo($foo :: HNil) }"""
      }
    }
    

    FooExample.scala

    import shapeless._
    import shapeless.test._
    
    import Foo._
    
    final class Bar[L <: HList](foo: Foo[L]) {
      def bar: L = foo.list
    }
    object FooExample {
    
      implicit def foo2Bar[L <: HList](foo: Foo[L]): Bar[L] = new Bar(foo)
    
      def main(args: Array[String]) {
        val expected = "test" :: HNil
        val notExpected = HNil
    
        val f1 = Foo.materializeFoo("test")
        sameTyped(expected)(f1.list)
    
        val f2 = "test".foo
        sameTyped(f2.list)(expected)
        sameTyped(f2.list)(notExpected)
    
        val l1: String :: HNil = f2.list
    
        //This actually compiles but throws a ClassCastException
        //val l2: HNil = f2.list
    
        illTyped("""Foo.materializeFoo("test").list""")
    
        sameTyped(expected)("test".foo.list)
        sameTyped(notExpected)("test".foo.list)
    
        sameTyped(expected)(f1.bar)
        illTyped("""Foo.materializeFoo("test").bar""")
    
        //I actually just want to call "test".foo.bar and have bar have the correct type
      }
    }
    

0 个答案:

没有答案