Skip to content

Instantly share code, notes, and snippets.

@travisbrown
Created March 6, 2013 19:52
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save travisbrown/5102474 to your computer and use it in GitHub Desktop.
Save travisbrown/5102474 to your computer and use it in GitHub Desktop.
Creating single abstract method class instances from function literals in Scala.
import scala.language.experimental.macros
import scala.language.higherKinds
import scala.reflect.macros.Context
object MacroSAM {
def sam[F[_, _], A, B](f: A => B) = macro sam_impl[F, A, B]
def sam_impl[F[_, _], A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)
(f: c.Expr[A => B])(implicit tag: c.WeakTypeTag[F[_, _]]) = {
import c.universe._
val anon = newTypeName(c.fresh())
val fab = appliedType(tag.tpe, List(weakTypeOf[A], weakTypeOf[B]))
c.Expr[F[A, B]](f.tree match {
case Function(valDef :: Nil, body) => Block(
ClassDef(
Modifiers(Flag.FINAL),
anon,
Nil,
Template(
TypeTree(fab) :: Nil,
emptyValDef,
List(
constructor(c),
DefDef(
Modifiers(),
newTermName("apply"),
Nil,
List(valDef :: Nil),
TypeTree(),
c.resetAllAttrs(body)
)
)
)
),
Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
)
})
}
def constructor(c: Context) = {
import c.universe._
DefDef(
Modifiers(),
nme.CONSTRUCTOR,
Nil,
Nil :: Nil,
TypeTree(),
Block(
Apply(
Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR),
Nil
) :: Nil,
c.literalUnit.tree
)
)
}
}
import MacroSAM.sam
trait MyFunc[-A, +B] {
def apply(a: A): B
}
object MyFuncExample {
def main(args: Array[String]) {
val x = sam[MyFunc, Int, Int] { a: Int => a }
println(x)
println(x(42))
}
}
@svenefftinge
Copy link

Would it be possible to write a macro such that I can write something like this:

val x:MyFunc[Int,Int] = { a => a }

and the SAM type is not hard-coded into the macro but works generically for all SAM types?
I.e. mimic the behavior of Xtend and Java 8?

@larsrh
Copy link

larsrh commented Mar 6, 2013

You'd need an implicit conversion from A => B to F[A, B] for that. However, I suspect that this is problematic – under some circumstances, scalac is not able to infer what F is supposed to be (i.e. it infers Nothing instead of MyFunc).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment