Created
March 6, 2013 19:52
-
-
Save travisbrown/5102474 to your computer and use it in GitHub Desktop.
Creating single abstract method class instances from function literals in Scala.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
) | |
) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | |
} | |
} |
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
Would it be possible to write a macro such that I can write something like this:
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?