2015-05-14 53 views
4

我有以下宏:如何在Scala編譯時檢查一些T是否是case類?

package macros 

import scala.reflect.macros.blackbox.Context 

object CompileTimeAssertions { 
    def mustBeCaseClass[T]: Unit = 
    macro CompileTimeAssertionsImpl.mustBeCaseClass[T] 
} 

object CompileTimeAssertionsImpl { 
    def mustBeCaseClass[T: c.WeakTypeTag](c: Context): c.Expr[Unit] = { 
    import c.universe._ 
    val symbol = c.weakTypeTag[T].tpe.typeSymbol 
    if (!symbol.isClass || !symbol.asClass.isCaseClass) { 
     c.error(c.enclosingPosition, s"${symbol.fullName} must be a case class") 
    } 
    reify(Unit) 
    } 
} 

它工作時不參與仿製藥,但是當他們失敗:

import macros.CompileTimeAssertions._ 
import org.scalatest.{Matchers, WordSpec} 

case class ACaseClass(foo: String, bar: String) 

class NotACaseClass(baz: String) 

class MacroSpec extends WordSpec with Matchers { 
    "the mustBeCaseClass macro" should { 
    "compile when passed a case class" in { 
     mustBeCaseClass[ACaseClass] 
    } 

    "not compile when passed a vanilla class" in { 
//  mustBeCaseClass[NotACaseClass] // fails to compile as expected. 
    } 

    "compile when working with generics" in { 
//  class CaseClassContainer[T] { mustBeCaseClass[T] } // fails to compile. 
//  new CaseClassContainer[ACaseClass] 
    } 
    } 
} 

編譯器錯誤都是我的

MacroSpec.CaseClassContainer.T must be a case class 

我想知道當CaseClassContainer被實例化時T是什麼。這甚至有可能嗎?如果你能提供一個例子嗎?

在此先感謝。

+1

不幸的是,你試圖做的事情是無法用這種方式實現的。 Def宏在你編寫它們的地方立即展開,因此在展開的時候,你所看到的所有內容都是T. –

+2

但是,如果你可以改變你的方法來結合類型類,你可能會得到某個地方。也就是說:你可以有一個'CaseClass [T]'類型的類,然後你可以說'class CaseClassContainer [T:CaseClass]'。查看https://github.com/scalamacros/macrology201/tree/part2,瞭解如何編寫這樣的宏。 –

+0

謝謝尤金!請隨意將這些複製並粘貼到答案中,並將其標記爲正確。不是答案,也許比我13分鐘前的地方要好很多。 –

回答

3

感謝尤金和特拉維斯的建議,我能夠通過類型類來解決這個問題。這裏的解決方案:

package macros 

import scala.reflect.macros.blackbox.Context 

trait IsCaseClass[T] 

object IsCaseClass { 
    implicit def isCaseClass[T]: IsCaseClass[T] = 
    macro IsCaseClassImpl.isCaseClass[T] 
} 

object IsCaseClassImpl { 
    def isCaseClass[T] 
     (c: Context) 
     (implicit T: c.WeakTypeTag[T]): c.Expr[IsCaseClass[T]] = { 
    import c.universe._ 
    val symbol = c.weakTypeTag[T].tpe.typeSymbol 
    if (!symbol.isClass || !symbol.asClass.isCaseClass) { 
     c.abort(c.enclosingPosition, s"${symbol.fullName} must be a case class") 
    } else { 
     c.Expr[IsCaseClass[T]](q"_root_.macros.IsCaseClassImpl[$T]()") 
    } 
    } 
} 

case class IsCaseClassImpl[T]() extends IsCaseClass[T] 

這裏是用法:

import macros.IsCaseClass 
import org.scalatest.{Matchers, WordSpec} 

case class ACaseClass(foo: String, bar: String) 

class NotACaseClass(baz: String) 

class CaseClassContainer[T: IsCaseClass] 

class MacroSpec extends WordSpec with Matchers { 
    "the code" should { 
    "compile" in { 
     new CaseClassContainer[ACaseClass] 
    } 

    "not compile" in { 
//  new CaseClassContainer[NotACaseClass] 
    } 
    } 
} 

值得注意的使用abort代替error。中止返回Nothing,而錯誤返回Unit。當宏沒有返回任何東西時後者很好。

相關問題