简体   繁体   中英

Using Scala's Macros to enforce type equality

I've been experimenting with MacroParadise ( here and here ), as well a few other newer features. Today while using TypeTags, I came to the realization that I can now do something like this to enforce type equality.

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

I then remembered that TypeTag implicits are compiler generated, and I had the idea that I may be able write a macro enabling more concise TypeTag usage like this:

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 
} 

I've only written a handful of macros in Lisp, and am stumbling around attempting to use the macro library. This lead me to several attempts, but they all end up expanding to something like Int =:= Int which doesn't work, or something like typeA =:= typeB where both are free(which also doesn't work).

This lead me to two questions: 1) Is this possible to do without the Context Bounds on foo (like written above)? 2) How do I correctly splice the Type s obtained by the implicits into the result expression?

It seems to me that macros and implicits should allow me to fetch the WeakTypeTag implicit and use its tpe member for splicing, since they both occur at compile time.

You can already enforce type equality by using the type =:=[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")
          ^

edit :

Sorry, got your question a little wrong, I guess. Your example almost works, but the compiler can't know, what A is, so it can't find an evidence of TypeTag[A] . If you add the context bound to the method definition it will work:

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 :

For your example you actually don't need TypeTag s at all, you can just use pattern matching, as you only use the first element:

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

But be aware, thath the pattern mathcing is non-exhaustive, because it does not handle Nil .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM