Use scala macros to copy method from class to companion object - scala

I'll get straight to the business.
Let's say that I have the following trait definition:
trait Routable{
def routing(): String
}
And I'm defining the following class:
case class MyEvent(name: String, age: Int) extends Routable{
override def routing(): String = "this is my routing key"
}
I'm trying to make a macro called routeOf[MyEvent] to return the routing key of the defined class.
I tried so many things for the last 3 days and I'm starting to wonder if it is possible at all...
My macro definition is:
def routeOf[T]: Any = macro RouteOfMacro.impl[T]
def impl[T: c.WeakTypeTag](c: whitebox.Context): c.Tree
But I can't find how to extract the method from the WeakTypeTag (and the internet is not full with examples).
So can it be done?

I'm trying to make a macro called routeOf[MyEvent] to return the
routing key of the defined class.
The way your code is, it's not the routing key of the defined class, but of the instance.
If you create multiple MyEvent, there is no guarantee their routing keys would be alike.
What you can do is create a companion object MyEvent that derives from Routable. I feel classTag or typeTag may be enough for this case - do you know those? No macros should be needed. But until I know the larger picture, hard to say how I'd approach it. Can you reveal more? :)

Related

Choosing between Trait and Object

I was trying to look into trait and object in scala when it seems like we can use trait and object to do a similar task.
What should be the guiding principles on when to use trait and when to use object?
Edit:
As many of you are asking for an example
object PercentileStats {
def addPercentile(df: DataFrame): DataFrame // implementation
}
trait PercentileStats {
def addPercentile(df: DataFrame): DataFrame // implementation
}
There is a Process class which can use the object
object Process {
def doSomething(df: DataFrame): DataFrame {
PercentileStats.addPercentile(df)
}
}
We can also make it use the trait
object Process with PercentileStats {
def doSomething(df: DataFrame): DataFrame {
addPercentile(df)
}
}
I think the real question here is Where do I put stand-alone functions?
There are three options.
In the package
You can put stand-alone functions in the outer package scope. This makes them immediately available to the whole package but the name has to be meaningful across the whole package.
def addPercentile(df: DataFrame): DataFrame // implementation
In an object
You can group stand-alone functions in an object to provide a simple namespace. This means that you have to use the name of the object to access the functions, but it keeps them out of the global namespace and allows the names to be simpler:
object PercentileStats {
def add(df: DataFrame): DataFrame // implementation
}
In a trait
You can group stand-alone functions in a trait. This also removes them from the package namespace, but allows them to be accessed without a qualifier from classes that have that trait. But this also makes the method visible outside the class, and allows them to be overridden. To avoid this you should mark them protected final:
trait PercentileStats {
protected final def addPercentile(df: DataFrame): DataFrame // implementation
}
Which is best?
The choice really depends on how the function will be used. If a function is only to be used in a particular scope then it might make sense to put it in a trait, otherwise the other options are better. If there are a number of related function then grouping them in an object makes sense. One-off functions for general use can just go in the package.
Object - is a class that has exactly one instance. It is created lazily when it is referenced, like a lazy val.
As a top-level value, an object is a singleton.
Traits - are used to share interfaces and fields between classes.
Classes and objects can extend while traits cannot be instantiated and therefore have no parameters.
So, it means that if you prefer singleton type implementation with no new instance happen then use Object but if you want to inherit implementation to other class or objects then you can use trait.
Traits: are equivalent to interfaces in Java. So you can use it to define public contracts like interfaces in Java. In addition, a trait can be used to share values (beside methods) between classes extends the trait.
Objects in Scala is actually quite flexible. Example use cases include:
singletons: If you think that your objects are singletons (exactly
one instance exists in the program), you can use object.
factory: for instance, companion object of a class can be used as factory for creating instances of the class.
to share static methods: for example, common utilities can be declared in one object.
You also have to consider how you would want to use / import it.
trait Foo {
def test(): String
}
object Bar extends Foo
import Bar._
Objects enable you to import rather than mix in your class.
It is a life saver when you want to mock - with scalamock - a class that mixes a lot of traits and expose more than 22 methods that you don't really need exposed in the scope.

Materialize implementation within package

When using a macro to materialize an implementation of a trait, I'd like to create the implementation within a package so that it has access to other package-private classes.
trait MyTrait[T]
object MyTrait {
implicit def materialize[T]: MyTrait[T] = macro materializeImpl[T]
def materializeImpl[T : c.WeakTypeTag](c: blackbox.Context): c.Expr[MyTrait[T]] = {
val tt = weakTypeTag[T]
c.Expr[MyTrait[T]](q"new MyTrait[$tt] {}")
}
}
Is it possible to materialize new MyTrait[$tt] {} within a particular package?
A macro has to expand into an AST which would compile in the place the macro call is in. Since package declarations are only allowed at top-level, and method calls aren't allowed there, the expanded tree can't create anything in another package.
As Alexey Romanov pointed out this is not possible directly. Still if you call only a few methods (and if you use macro, most probably this is so), one possible (but not perfect) workaround might be creating a public abstract class or trait that extends the target trait and "publishes" all the required package private methods as protected proxies. So you can create instances in your macro from inheriting from that abstract class rather than trait. Obviously this trick effectively "leaks" those methods to anyone but thanks to reflection anyone can call any method if he really wants. And abusing this trick will show as deliberate effort to circumvent your separation as the usage of the reflection.

How do I get an appropriate typeclass instance at runtime?

Part I
Suppose I have a type class trait Show[T] { def print(t: T): String } with instances for String and Int. Suppose I have a value whose specific type is known only at runtime:
val x: Any = ...
How do I get the appropriate typeclass instance (at runtime, since we don't know the type statically) and do something with it.
Note that it's inadequate to define a method that literally just gives us the typeclass instance:
def instance(x: Any): Show[_]
Since Show.print requires statically known argument type T we still can't do anything with the result of instance. So really, we need to be able to dynamically dispatch to an already-defined function that uses the instance, such as the following:
def display[T](t: T)(implicit show: Show[T]) = "show: " + show.print(t) + "\n"
So assuming display is defined, how do we invoke display, passing along an appropriate Show instance. I.e. something that invokes display(x) properly.
Miles Sabin accomplishes this here using runtime compilation (Scala eval), as an example of "staging", but with only spare documentation as to what's going on:
https://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/staging.scala
Can Miles's approach be put into a library? Also, what are the limitations of this approach e.g. with respect to generic types like Seq[T]?
Part II
Now suppose T is bounded by a sealed type (such that it's possible to enumerate all the sub-types):
trait Show[T <: Foo]
sealed trait Foo
case class Alpha(..) extends Foo
case class Beta(..) extends Foo
In this case, can we do it with a macro instead of runtime compilation? And can this functionality be provided in some library?
I mostly care about Scala 2.12, but it's worth mentioning if a solution works in 2.11 or 2.10.

"dynamically" creating case classes with macros

I would like to create a macro generated hierarchy of sealed abstract and case classes. There was an example similar to this with http://docs.scala-lang.org/overviews/macros/typemacros.html but is is now obsolete. Is this still possible?
I think it would be incredibly powerful to generate a type safe AST for some specified grammar. Ideally with an IDE able to resolve all the classes.
First for some shameless self-promotion: Eugene Burmako and I are giving a talk on type providers, a closely related topic, at Scalar 2014 tomorrow, and I encourage you to take a look at the example project we put together for the talk if you're interested in this kind of thing.
While type macros are no longer supported, you can accomplish essentially the same thing with macro annotations from macro paradise (which is available as a plugin for Scala 2.10 and 2.11):
import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
import scala.reflect.macros.Context
// Add constructor arguments here.
class expand extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro Expander.expand_impl
}
object Expander {
def expand_impl(c: Context)(annottees: c.Expr[Any]*) = {
import c.universe._
annottees.map(_.tree) match {
case List(q"trait $name") => c.Expr[Any](
// Add your own logic here, possibly using arguments on the annotation.
q"""
sealed trait $name
case class Foo(i: Int) extends $name
case class Bar(s: String) extends $name
case object Baz extends $name
"""
)
// Add validation and error handling here.
}
}
}
And then:
scala> #expand trait MyADT
defined trait MyADT
defined class Foo
defined class Bar
defined module Baz
You can add arguments to the annotation that will be available at compile time, allowing you to parse an external resource that you can use to generate the implementation of the ADT, for example.
Macro annotations are very experimental and their status is still up in the air—there's no guarantee that they'll ship with Scala 2.12, for example. Something similar (although not quite so clean) is possible using plain old def macros and structural types—see the example project linked above for more detail and some demonstrations. In any case, this kind of mechanism is of interest to many people, including the developers of Scala's macro system, so even if macro annotations disappear at some point down the road, there's likely to be some way to accomplish what you've described here.

Scala: convert a return type into a custom trait

I've written a custom trait which extends Iterator[A] and I'd like to be able to use the methods I've written on an Iterator[A] which is returned from another method. Is this possible?
trait Increment[+A] extends Iterator[A]{
def foo() = "something"
}
class Bar( source:BufferedSource){
//this ain't working
def getContents():Increment[+A] = source getLines
}
I'm still trying to get my head around the whole implicits thing and not having much like writing a method in the Bar object definition. How would I go about wrapping such an item to work in the way I'd like above?
Figured it out. Took me a few tries to understand:
object Increment{
implicit def convert( input:Iterator[String] ) = new Increment{
def next() = input next
def hasNext() = input hasNext
}
}
and I'm done. So amazingly short.
I don't think that this is possible without playing tricks. A mixin inheritance happens at compile time, when it can be type-checked statically, and it is always targeted at another class, trait, etc. Here you try to tack a trait on an existing object "on the fly" at runtime.
There are workarounds like implicit conversions, or maybe proxies. Probably the "cleanest" way would be to make Increment a wrapper class delegating to an underlying Iterator. Depending on your use case there might be other solutions.