[英]How to test type conformance of higher-kinded types in Scala
我試圖測試兩個“容器”是否使用相同的高級類型。 看下面的代碼:
import scala.reflect.runtime.universe._
class Funct[A[_],B]
class Foo[A : TypeTag](x: A) {
def test[B[_]](implicit wt: WeakTypeTag[B[_]]) =
println(typeOf[A] <:< weakTypeOf[Funct[B,_]])
def print[B[_]](implicit wt: WeakTypeTag[B[_]]) = {
println(typeOf[A])
println(weakTypeOf[B[_]])
}
}
val x = new Foo(new Funct[Option,Int])
x.test[Option]
x.print[Option]
輸出是:
false
Test.Funct[Option,Int]
scala.Option[_]
但是,我希望一致性測試能夠成功。 我究竟做錯了什么? 我如何測試更高級的類型?
澄清
在我的例子中,我正在測試的值(示例中的x: A
)來自宏中的List[c.Expr[Any]]
。 所以任何依賴於靜態分辨率的解決方案(就像我給出的那樣)都無法解決我的問題。
它是類型參數定義中使用的下划線與其他地方之間的混合。 TypeTag[B[_]]
的下划線表示存在類型,因此您獲得的標簽不是B
,而是存在於其上的存在包裝,如果沒有手動后處理,這幾乎是無用的。
因此,需要原始B
標簽的typeOf[Funct[B, _]]
無法使用包裝器的標簽而感到沮喪。 令我感到沮喪的意思是它拒絕在范圍內拼接標簽並因編譯錯誤而失敗。 如果您使用weakTypeOf
,那么那個將成功,但它將為它無法拼接的所有內容生成存根,使得結果對於子類型檢查無效。
看起來在這種情況下我們真的達到了Scala的極限,因為我們無法在WeakTypeTag[B]
引用原始B
,因為我們在Scala中沒有類型的多態性。 希望像DOT這樣的東西可以避免這種不便,但與此同時你可以使用這種解決方法(它不是很漂亮,但我還沒有能夠提出一種更簡單的方法)。
import scala.reflect.runtime.universe._
object Test extends App {
class Foo[B[_], T]
// NOTE: ideally we'd be able to write this, but since it's not valid Scala
// we have to work around by using an existential type
// def test[B[_]](implicit tt: WeakTypeTag[B]) = weakTypeOf[Foo[B, _]]
def test[B[_]](implicit tt: WeakTypeTag[B[_]]) = {
val ExistentialType(_, TypeRef(pre, sym, _)) = tt.tpe
// attempt #1: just compose the type manually
// but what do we put there instead of question marks?!
// appliedType(typeOf[Foo], List(TypeRef(pre, sym, Nil), ???))
// attempt #2: reify a template and then manually replace the stubs
val template = typeOf[Foo[Hack, _]]
val result = template.substituteSymbols(List(typeOf[Hack[_]].typeSymbol), List(sym))
println(result)
}
test[Option]
}
// has to be top-level, otherwise the substituion magic won't work
class Hack[T]
精明的讀者會注意到我在foo
的簽名中使用了WeakTypeTag
,即使我應該能夠使用TypeTag
。 畢竟,我們在一個表現良好的Option
上調用foo,因為它不涉及未解決的類型參數或為TypeTag
帶來問題的本地類。 不幸的是,由於https://issues.scala-lang.org/browse/SI-7686 ,它並不那么簡單,所以即使我們不需要,我們也不得不使用弱標簽。
以下是適用於我給出的示例(可能對其他人有幫助)的答案,但不適用於我的(非簡化)案例。
竊取@ pedrofurla的提示,並使用類型類:
trait ConfTest[A,B] {
def conform: Boolean
}
trait LowPrioConfTest {
implicit def ctF[A,B] = new ConfTest[A,B] { val conform = false }
}
object ConfTest extends LowPrioConfTest {
implicit def ctT[A,B](implicit ev: A <:< B) =
new ConfTest[A,B] { val conform = true }
}
並將此添加到Foo
:
def imp[B[_]](implicit ct: ConfTest[A,Funct[B,_]]) =
println(ct.conform)
現在:
x.imp[Option] // --> true
x.imp[List] // --> false
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.