Not sure how to properly formulate this (hence, how to look it up), but here goes:
I understand how a method applied to an object can become a function object. For example:
case class User(name: String, age: Int)
val user = User("", 0)
user.name _ // () => String
So, if I had a method def meth(f: () => String)
, I could do: meth(user.name _)
Is is possible to define a type that has as instances the methods of class User
? (the function objects obtained from these methods, more precisely)
In order words, what would the type of f
in def meth(f: ???)
be, in order to be able to do this: meth(user.name _)
and meth(user.age _)
Thanks!
I guess the only way to do something like that is to use macro's. I once created a macro that does the following (note that some details changed in the current implementation and thus are empty).
case class Metadata(instance: AnyRef, name: String) {
def value = ??? // use reflection to invoke `name` on `instance`
}
object Metadata extends ((AnyRef, String) => Metadata) {
implicit def anyToMetadata(sym: Any): Metadata =
macro MetadataMacro.anyToMetadataImpl
}
private[staticReflection] object MetadataMacro {
def anyToMetadataImpl(c: Context)(sym: c.Expr[Any]): c.Expr[Metadata] = {
import c.universe._
val metadata = // match the tree and create the appropriate metadata instance
c.Expr(metadata)
}
}
In code you would then use it like this:
case class User(name:String)
def test(metadata:Metadata) {
println(metadata.name + "->" + metadata.value)
}
val u = User("test")
test(u.name) // name -> test
The code as it was valid almost a year ago can be found here: ee/scala/staticReflection/Metadata.scala . More info about macros as they are now .
If this is what you were looking for, please let me know so I can see if I can convert the original to a working version.
I managed to get the old one working. To use it simply copy-past the code into a separate project (I use Eclipse) and then link the projects via the Java Build Path
. Working version:
package ee.scala.staticReflection
import scala.language.experimental.macros
import scala.reflect.macros.Context
import language.implicitConversions
case class Metadata(instance: AnyRef, name: String) {
// any comments on how to improve this part are welcome
val runtimeUniverse = scala.reflect.runtime.universe
val mirror = runtimeUniverse.runtimeMirror(getClass.getClassLoader)
val instanceMirror = mirror.reflect(instance)
val method = instanceMirror.symbol.selfType.member(runtimeUniverse newTermName name).asMethod
val methodMirror = instanceMirror.reflectMethod(method)
def value = methodMirror()
}
object Metadata extends ((AnyRef, String) => Metadata) {
implicit def anyToMetadata(sym: Any): Metadata = macro MetadataMacro.anyToMetadataImpl
}
private[staticReflection] object MetadataMacro {
def anyToMetadataImpl(c: Context)(sym: c.Expr[Any]): c.Expr[Metadata] = {
import c.universe._
def createMetadataInstance(select: Select): Tree =
treeBuild.mkMethodCall(
c.mirror.staticModule("ee.scala.staticReflection.Metadata"),
newTermName("apply"),
List(select.qualifier, Literal(Constant(select.name.toString))))
val metadata = sym.tree match {
//normal select
case select: Select => createMetadataInstance(select)
//could be a call using a right associative operator
case Ident(name) =>
c.enclosingMethod.collect {
case ValDef(_, refName, _, select: Select) if refName == name => createMetadataInstance(select)
}
.headOption
.getOrElse(throw new Exception("Could not find ValDef for " + name))
case _ => throw new Exception("Could not create metadata")
}
c.Expr(metadata)
}
}
To apply all of the above stuff to the question of the original poster. You could use it like this
case class User(name: String, age: Int)
val user = User("", 0)
def meth(metadata: Metadata) = {
println(metadata.name + "->" + metadata.value)
}
meth(user.name)
meth(user.age)
Note that the solution is different from what was proposed, but within the meth
function you can do the same (a bit more even).
I believe what you're looking for is Scala's "structural type" (not really a Scala innovation).
It captures non-nominal typing but its implementation on the JVM necessitates the use of reflection at the point in your code where you want to actually access or invoke the members dictated by the structural type. Hence there's a pretty non-trivial overhead associated with using structural types in Scala. (I recall hearing talk about implementing some kind of cache for the required reflection-derived information, but I don't know if that was ever implemented).
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.