2013-07-20 3 views
9

Я пытаюсь проверить, используют ли два «контейнера» один и тот же более высокий тип. Посмотрите на следующий код:Как проверить соответствие типов типов более высокого качества в 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]] в Macro. Поэтому любое решение, основанное на статическом разрешении (как указано мной), не решит мою проблему.

+1

Для подтверждения вашего ожидания немного: 'неявно [Funct [Option, Int] <: pedrofurla

+0

Из https: //gist.github.com/xeno-by/6054650 Я вижу, что тип reifier не видит тега типа для B, поэтому вы не получите правильный тип из weakTypeOf [Funct [B, _]]. –

+0

Хорошо, теперь я вижу, в чем проблема. Это снова смешение между подчеркиваниями, используемыми в определениях параметров типа и в других местах. Подчеркивание в 'TypeTag [B [_]]' означает экзистенциальный тип, поэтому вы получаете тег типа не для 'B', а для экзистенциальной оболочки поверх него. Следовательно, 'typeOf [Funct [B, _]]' не может использовать этот тег типа и расстраивается. –

ответ

8

Это путаница между подчеркиванием используется в определениях параметров типа и в других местах. Подчеркивание в TypeTag[B[_]] означает экзистенциальный тип, поэтому вы получаете тег не для B, а для экзистенциальной оболочки поверх него, что практически бесполезно без ручной постобработки.

Следовательно, typeOf[Funct[B, _]], которому требуется бирка для сырья B, не может использовать тег для обертки и расстраиваться. Из-за того, что я расстраиваюсь, я имею в виду, что он отказывается связывать тег в области видимости и терпит неудачу с ошибкой компиляции. Если вместо этого вы используете weakTypeOf, то этот будет преуспевать, но он будет генерировать заглушки для всего, что не удалось сплайсировать, делая результат бесполезным для проверки подтипов.

Похоже, что в этом случае мы действительно попадаем в рамки Scala в том смысле, что нам не нужно ссылаться на raw B в WeakTypeTag[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] 

Проницательный читатель заметит, что я использовал WeakTypeTag в подписи foo, хотя я должен быть в состоянии использовать TypeTag. В конце концов, мы вызываем foo на Option, который является хорошим типом, в том смысле, что он не включает нерешенные параметры типа или локальные классы, которые создают проблемы для TypeTag. К сожалению, это не так просто из-за https://issues.scala-lang.org/browse/SI-7686, поэтому мы вынуждены использовать слабый тег, хотя нам и не нужно.

+0

Может ли один реферат над Foo? – Edmondo1984

5

Ниже приведен ответ, который работает для примера, который я дал (и мог бы помочь другим), но не относится к моему (не упрощенному) случаю.

Кража из намек @ 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 
Смежные вопросы