How do I pass an argument to the macro annotation? - scalameta

I want get StaticAnnotation's parameters defined as:
class Log(logTag: List[LogTag] = Info() :: Nil )
(implicit logger: String => Unit = a => {println(a)})
extends scala.annotation.StaticAnnotation {
inline def apply(defn: Any): Any = meta {
val logTag = ??? //todo
val logger = ??? //todo
}
}
How to get the logTag and logger Function values in meta block?
I have seem meta tutorials about this point. But the Lit just refers Int/Double,etc, rather then custom class type.

You can deconstruct this as a scala.meta.Tree to get the class parameters. See http://scalameta.org/tutorial/#HowdoIpassanargumenttothemacroannotation%3F for an example

Related

Is there some way in scala that I can return a type?

I have a lot of classes such as DataFrameFlow, TextFlow, RDDFlow. They all derive from base class Flow.
Now I want to write a function judgeFlow which can read from a path: String and return something representing exact Flow type from which I can create corresponding instance. The whole code seems like the following
def judgeFlow(path:String) = /*1*/ {
Flow.getStoreType(path) match {
case StoreType.tdw =>
DataFrameFlow
case StoreType.hdfs =>
TextFlow
}
}
def createFlow(typeInfo:/*2*/) = /*3*/{
new typeInfo()
}
However, I don't know how to write in place 1, 2 and 3.
EDIT
Knowing how to construct them is not enough here, because I also want the following:
pattern matching through typeInfo
some ways to do asInstanceOf
EDIT 2
Definition of Flow
abstract class Flow(var outputName: String) extends Serializable{
def this() = this("")
...
}
Definition of DataFrameFlow
class DataFrameFlow(d: DataFrame, path: String) extends Flow {
var data: DataFrame = d
def this(data: DataFrame) = this(data, "")
def this(path: String) = this(null, path)
def this() = this(null, "")
...
}
Pattern matching can't return different types from different cases. The type returned by pattern matching is the least upper bound of types returned in cases.
When someone wants to return different types, most probably he/she wants a type class.
sealed abstract class Flow
class DataFrameFlow extends Flow
class TextFlow extends Flow
class RDDFlow extends Flow
trait JudgeFlow[In] {
type Out <: Flow
def judgeFlow(in: In): Out
}
object JudgeFlow {
implicit val `case1`: JudgeFlow[???] { type Out = DataFrameFlow } = ???
implicit val `case2`: JudgeFlow[???] { type Out = TextFlow } = ???
implicit val `case3`: JudgeFlow[???] { type Out = RDDFlow } = ???
}
def judgeFlow[In](in: In)(implicit jf: JudgeFlow[In]): jf.Out = jf.judgeFlow(in)
But the trouble is that types are resolved at compile time. You seem to want to choose a case based on a value of string i.e. at runtime. So you can't return more specific types than just Flow at compile time.
flatMap with Shapeless yield FlatMapper not found
It's hard to guess your use case completely.
But using Scala reflection you can try
import scala.reflect.runtime.universe._
import scala.reflect.runtime.currentMirror
def judgeFlow(path:String): Type = {
Flow.getStoreType(path) match {
case StoreType.tdw =>
typeOf[DataFrameFlow]
case StoreType.hdfs =>
typeOf[TextFlow]
}
}
def createFlow(typeInfo: Type): Flow = {
val constructorSymbol = typeInfo.decl(termNames.CONSTRUCTOR).asMethod
val classSymbol = typeInfo.typeSymbol.asClass
val classMirror = currentMirror.reflectClass(classSymbol)
val constructorMirror = classMirror.reflectConstructor(constructorSymbol)
constructorMirror().asInstanceOf[Flow]
}

Scalameta Decl.Def not works on a trait def method

I'm use Scalameta(v1.8.0) annotation to a def declaration:
trait MyTrait {
#MyDeclDef
def f2(): Int
}
The annotation class defined just return input, as this:
import scala.meta._
class MyDeclDef extends scala.annotation.StaticAnnotation {
inline def apply(defn: Any): Any = meta {
defn match {
case defn: Decl.Def =>
defn
case _ =>
println(defn.structure)
abort("#MyDeclDef most annotate a Decl.Def")
}
}
}
some compiler error encounter:
Error:'=' expected but eof found.
def f2(): Unit
Error:illegal start of simple expression
def f2(): Unit
Besides, If I use Decl.Var to var v2: Int it works fine.
How to right annotate a trait def? Thanks
I didn't work with scala-meta before and tried your example. It looks like we have to implement this method in the macros. For example, if we specify a default implementation:
class MyDeclDef extends scala.annotation.StaticAnnotation {
inline def apply(defn: Any): Any = meta {
defn match {
case defn: Decl.Def =>
val q"def $name(): $tpe" = defn
q"def $name(): $tpe = 1"
we will be able to create an instance of MyTrait and print a value of the f2 method :)
val x = new MyTrait
println(x.f2) // Prints 1
var v2: Int works fine, because it fits to case _ in which we abort the compilation.

Accessing implicit parameters explicitly at runtime

I'm trying to access an implicit parameter for a generic type. Scala is able to find the implicit just fine in the straightforward case by calling a method with an explicit generic type, such as with printGenericType[Person] below.
However, if I create a TypeTag[Person] and pass it to printGenericTypeGivenTypeTag, Scala is unable to find the implicit parameter.
case class Person(name: String)
case class Animal(age: Int)
implicit val p = Person("cory")
implicit val a = Animal(2)
def main(args: Array[String]): Unit = {
// Can find the implicit Person, prints "Hello Person(cory)"
printGenericType[Person]
// Can find the implicit Animal, prints "Hello Animal(2)"
printGenericType[Animal]
// See comment below
printNamedType("Person")
printNamedType("Animal")
}
def printGenericType[T](implicit t: T) = {
println(s"Hello $t")
}
def printGenericTypeGivenTypeTag[T](typeTag: TypeTag[T])(implicit t: T) = {
println(s"Hello $t")
}
def printNamedType[T](name: String) = {
val typetag: TypeTag[T] = getTypeTag[T](name)
// Cannot find the implicit of type T, compiler error
printGenericTypeGivenTypeTag(typetag)
}
def getTypeTag[T](name:String): TypeTag[T] = ... //Implementation irrelevant
If I understand correctly, Scala locates implicit parameters at compile time, so it makes sense that it can't find an implicit parameter for the generic type T at compile time.
However, I know that an implicit instance of T does exist. Is it possible to rewrite printGenericTypeGivenTypeTag in such a way as to find the implicit value for T? At runtime, the method has access to the actual type of T, so it seems it should be able to locate an implicit parameter of the same type that is in scope.
For the curious, the reasoning behind this is to avoid this:
name match {
case "Person" => printGenericType[Person]
case "Animal" => printGenericType[Animal]
}
To answer the question
You're not really wanting to pass the T implicitly, but rather the TypeTag, and not the T. Here's what I mean, and you're probably better off with an implicit value class.
implicit class GenericPrinter[T](val obj: T) extends AnyVal {
def printGenericType()(implicit tag: TypeTag[T]) = {
// do stuff with the tag
Console.println("Hello $obj")
}
}
val x: Person = Person(...)
x.printGenericType
Now solving the real problem
If you are trying to print case classes, I'd probably go down the implicit macro route for added convenience. It's really trivial to write up a macro that does this for us, e.g output a debug string based on all the constructor params of an arbitrary case class.
trait DeepPrinter[T <: Product with Serializable] {
/**
* Prints a deeply nested debug string for a given case class.
* This uses implicit macros to materialise the printer type class.
* In English, when we request for an implicit printer: DeepPrinter[T],
* the pre-defined compile time macro will generate this method for us
* based on the fields of the given case class.
*
* #param sep A separator to use to delimit the rows in a case class.
* #return A fully traced debug string so we can see how a case class looks like.
*/
def debugString(sep: String = "\n"): String
}
object DeepPrinter {
implicit def deepPrinter[T <: Product with Serializable] = macro DeepPrinterImpl.deepPrinterImpl[T]
}
And the macro is pretty trivial, looks kind of like this.
import language.experimental.macros
import scala.reflect.macros.blackbox
#macrocompat.bundle
class DeepPrinterImpl(val c: blackbox.Context) {
import c.universe._
object CaseField {
def unapply(symbol: TermSymbol): Option[(Name, Type)] = {
if (symbol.isVal && symbol.isCaseAccessor) {
Some(symbol.name -> symbol.typeSignature)
} else {
None
}
}
}
def fields(tpe: Type): Iterable[(Name, Type)] = {
tpe.decls.collect { case CaseField(nm, tpeSn) => nm -> tpeSn }
}
def materialize[T : c.WeakTypeTag]: c.Expr[DeepPrinter[T]] = {
val tpe = weakTypeOf[T]
val (names, types) = fields(tpe).unzip
// change the package name to the correct one here!
val tree = q"""
new com.bla.bla.DeepPrinter[$tpe] {
def debugString(sep: String = "\n") = Seq(..$names).mkString(sep)
}
"""
c.Expr[DeepPrinter[T]](tree)
}
}

Scala: specifying a generic type that is constructible from another

I would like to do something which, more or less, boils down to the following:
def foo[S](x: String): S = S(x) // S(x) does not compile
So that if I have:
case class S1(x:String)
case class S2(x:String)
...
case class Sn(x:String)
I can write foo[Sx]("bar") to get Sx("foo").
Is there any way to specify that an instance of a class should be constructible from an instance of another (in this example String) and to actually invoke the constructor in a generic way?
You may use reflection (see #Ben Reich answer for detailed answer)
def foo[S:ClassTag](x: String): S = {
val runtimeClass = implicitly[ClassTag[S]].runtimeClass
val constructor = runtimeClass.<get a constructor with single String argument>
constructor(x) .asInstanceOf[S]
}
Or a type class that can construct an instance:
trait CanConstruct[S] {
def apply(x:String):S
}
def foo[S:CanConstruct](x: String): S = {
val constructor = implicitly[CanConstruct[S]]
constructor(x).asInstanceOf[S]
}
UPD You would need an instance of the type class for every type you wish to construct:
implicit val s1constructor = new CanConstruct[S1] { def apply(x:String) = S1(x) }
...
Also it seems to be the case for conversion functions:
implicit val convertStringToS1 = S1(_)
implicit val convertStringToS2 = S2(_)
...
Using reflection:
import reflect.ClassTag
def foo[S: ClassTag](x: String) = implicitly[ClassTag[S]]
.runtimeClass
.getConstructors
.map(a => a -> a.getParameters)
.collectFirst {
case (constructor, Array(p)) if p.getType == classOf[String]
=> constructor.newInstance(x).asInstanceOf[S]
}
Which will return an Option[S], if the proper constructor is found.
I sort of solved it with a macro:
object Construct {
import scala.language.experimental.macros
import scala.reflect.macros.Context
def construct[A,B](x:B):A = macro constructImpl[A,B]
def constructImpl[A: c.WeakTypeTag,B](c:Context)(x:c.Expr[B]) = {
import c.universe._
c.Expr[A](q"""new ${c.weakTypeOf[A]}(${x.tree})""")
}
}
I now can write things like:
case class Foo(x:String)
case class Bar(x:Int)
construct[Foo,String]("foo")
construct[Bar,Int](42)
It would be nice to find a way to avoid having to write the second type parameter, though.

How to use scalaz's `Tagged Type` to replace my type alias?

I want to define objects as functions and depend it in other functions:
type FetchPage = String => String
type FindImages = String => List[String]
object WillFetchPage extends FetchPage {
def apply(url:String):String = /* get content of the url */
}
class WillFindImages(fetchPage: FetchPage) extends FindImages {
def apply(url:String):List[String] = {
val html = fetchPage(url)
// get image srcs from the <img> tags
}
}
Then I can inject WillFetchPage to WillFindImages:
val findImages = new WillFindImages(WillFetchPage)
Also test WillFindImages easily by injecting a mock function:
val testFindImages = new WillFindImages(_ => "html-have-3-images")
val images = testFindImages("any-url")
// assertion
You can see the type alias FetchPage is just a type alias, so I can pass other String => String functions to WillFindImages by mistake, so I'm looking for a type-safe solution.
Then I heard of Tagged type from scalaz: http://eed3si9n.com/learning-scalaz/Tagged+type.html
The example is exciting:
sealed trait KiloGram
def KiloGram[A](a: A): A ## KiloGram = Tag[A, KiloGram](a)
val mass = KiloGram(20.0)
2 * mass
You can see the mass here is actually a double 20.0, but it has some unique type.
I want to use it to improve my code, but sadly I can't find a way to do it. I tried:
object FetchPage extends ((String => Try[String]) ## FetchType)
But it provides an compilation error:
Error:(18, 51) class type required but String => scala.util.Try[String] with
scalaz.Tagged[object_as_func.FetchType] found
object FetchPage extends ((String => Try[String]) ## FetchType) {
^
How to fix it?
What scalaz version do you use? It's a bit different in 7.0.6 and 7.1.0. Here is example for 7.1.0
import scalaz.##
object PageFetcher
type FetchPage = String => String
type FindImages = String => List[String]
val WillFetchPage: FetchPage ## PageFetcher.type =
scalaz.Tag[FetchPage, PageFetcher.type](url => "Content")
class WillFindImages(fetchPage: FetchPage ## PageFetcher.type) extends FindImages {
def apply(url: String): List[String] = scalaz.Tag.unwrap(fetchPage)(url).grouped(1).toList
}
val images = new WillFindImages(WillFetchPage)("url")
println(images)
val testFetcher: FetchPage ## PageFetcher.type =
scalaz.Tag[FetchPage, PageFetcher.type](url => "TestContent")
val testImages = new WillFindImages(testFetcher)("url")
println(testImages)