2013-02-21 3 views
4

Я экспериментировал с MacroParadise (here и here), а также несколькими другими более новыми функциями. Сегодня, используя 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) Как правильно спланировать Type s, полученный имплицитами в выражение результата?

Мне кажется, что макросы и импликации должны позволять мне использовать неявный код WeakTypeTag и использовать его член tpe для сращивания, поскольку они оба происходят во время компиляции.

ответ

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 с на всех, вы можете просто использовать поиск по шаблону, как вы использовать только первый элемент:

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 

Но помните, Тат шаблон mathcing не является исчерпывающим, поскольку он не обрабатывает Nil.

+0

Это нормально для утверждения равенства только двух типов, но свидетельство типа =: = не может использоваться в теле из-за стирания. Вызов foo [Int] и foo [String] идентичны во время выполнения, вам нужен механизм для восстановления стертой информации (т. Е. TypeTags). Я заинтересован в краткой формулировке равенства типов между этими двумя типами, независимо от стирания. Если вы замените 'typeEq [ty1, ty2]' на 'ty1 =: = ty2', приведенный выше пример не будет компилироваться. Также стоит отметить, что =: = на самом деле метод на 'scala.runtime.reflect.Type', а не class =: =, который используется для неявного свидетеля. – jroesch

+0

обновил мое сообщение – drexin

+0

Спасибо, это действительно решает мой пример (хотя и надуманный). Меня больше интересует макрокоманда. У меня все еще была проблема с правильной сортировкой типов. Это более важно для случая, когда 'A' ​​может не совпадать с типом' xs.head' или с другими структурами. – jroesch

Смежные вопросы