2013-02-21 193 views
4

我一直在嘗試MacroParadise(herehere)以及一些其他更新的功能。今天,在使用TypeTags時,我意識到我現在可以做這樣的事情來強制執行類型相等。使用Scala的宏來強制執行類型相等

def typeEq[A: TypeTag, B: TypeTag]: Boolean = { 
    implicitly[TypeTag[A]].tpe =:= implicitly[TypeTag[B]].tpe 
} 

然後我想起TypeTag implicits產生的編譯器,我有我或許可以寫一個宏實現更簡潔TypeTag使用這樣的想法:

def foo[A](xs: List[A]): String = xs match { 
    case y :: ys if typeEq[A, String] => y 
    case y :: ys if typeEq[A, Int] => y.toString 
} 

我只寫Lisp中的一些宏,以及試圖使用宏庫時磕磕絆絆。這導致我進行了幾次嘗試,但最終都擴大到了Int =:= Int這種不起作用的東西,或者像typeA =:= typeB這樣的東西都是免費的(這也不起作用)。

這導致我有兩個問題: 1)這可能沒有foo上的背景界限(如上面寫的)? 2)如何將implicits獲得的Type正確拼接到結果表達式中?

在我看來,宏和暗示應該允許我獲取隱含的WeakTypeTag並使用其成員進行拼接,因爲它們都發生在編譯時。

回答

3

您已經可以通過使用類型=:=[From,To]執行類型相等:

def bar[A,B](a:A,b:B)(implicit ev: A =:= B)= (a,b) 

bar(1,1) 
res0: (Int, Int) = (1,2) 

bar(1,"3") 

error: could not find implicit value for parameter ev: =:=[Int,java.lang.String] 
     bar(1,"3") 
     ^

編輯

對不起,有你的問題有點不對勁,我猜。你的例子幾乎可行,但編譯器不知道,A是什麼,所以它找不到TypeTag[A]的證據。如果添加綁定方法定義的情況下,將工作:

def foo[A : TypeTag](xs: List[A]) = xs match { 
    case _ if typeEq[A, String] => "yay" 
    case _ => "noes" 
} 

scala> foo(List(2)) 
res0: String = noes 

scala> foo(List("")) 
res1: String = yay 

EDIT2

爲了您的例子中,你其實並不需要TypeTag s的一切,你可以只使用模式匹配,因爲你只用第一個元素:

def foo[A](xs: List[A]): String = xs match { 
    case (y: String) :: _ => y 
    case y :: _ => y.toString 
} 

scala> foo(List(1,2,3)) 
res6: String = 1 

scala> foo(List("foo")) 
res7: String = foo 

但要知道,thath模式mathcing是不可窮盡的,因爲它不處理Nil

+0

這對於在兩種類型上聲明相等性很好,但由於擦除,=:=類型見證在主體中不可用。調用foo [Int]和foo [String]在運行時是相同的,您需要一種恢復擦除信息的機制(即TypeTags)。不管是否擦除,我都希望有一種簡潔的方式來說明兩種類型之間的類型相等。如果用'ty1 =:= ty2'替換'typeEq [ty1,ty2]',則上面的示例不會編譯。值得注意的是=:=實際上是'scala.runtime.reflect.Type'上的一個方法,而不是class =:=,它用於隱含的見證。 – jroesch 2013-02-21 09:32:11

+0

更新了我的帖子 – drexin 2013-02-21 09:51:00

+0

謝謝,這確實解決了我的例子(儘管是一個人爲的)。我對宏寫作部分更感興趣。我仍然無法正確拼接類型。這對於'A'可能與'xs.head'類型或其他結構不同的情況更爲重要。 – jroesch 2013-02-21 20:46:02