3 votes

Extraction d'un symbole à partir de la valeur de retour d'une fonction

En utilisant des macros, je souhaite obtenir une solution générale pour faire référence à Symbol de champs et de sous-champs d'un arbre de données de classe de cas.

Inspiré par ce gist J'ai créé le squelette suivant :

import language.experimental.macros
import reflect.runtime.universe._
import reflect.macros.Context

object SubRef {

  case class SubRef[ SourceT, TargetT ]( symbol : Symbol )

  implicit def functionToSubRef
    [ SourceT, TargetT ]
    ( f : SourceT => TargetT ) 
    : SubRef[ SourceT, TargetT ]
    = macro functionToSubRefMacro[ SourceT, TargetT ]

  def functionToSubRefMacro
    [ SourceT : c.WeakTypeTag, TargetT : c.WeakTypeTag ]
    ( c : Context )
    ( f : c.Expr[ SourceT => TargetT ] )
    : c.Expr[ SubRef[ SourceT, TargetT ] ]
    = ???

}

Je m'attends à ce que cette solution fonctionne de la manière suivante :

case class A( b : B )
case class B( c : Int )

val ref : SubRef[ A, Int ] = _.b.c

La conversion de la macro doit échouer si la fonction transmise :

  • N'est pas une expression unique
  • Ne renvoie pas la valeur d'un symbole qui est un sous-champ du symbole source, c'est-à-dire le symbole targetSymbol.owner[.owner...] == sourceSymbol

Quelle devrait être l'implémentation de la macro manquante ?

2voto

Eugene Burmako Points 8453

Mis à part le fait que votre notation ne fonctionnera pas en 2.10 comme l'a souligné Simon, l'écriture de cette macro devrait être facile. Il semble que vous sachiez déjà comment effectuer la validation, je vais donc omettre cette partie.

Le véritable défi consiste à convertir un artefact de réflexion à la compilation ( c.universe.Symbol ) à un artefact de réflexion en cours d'exécution ( ru.Symbol ). Cela peut se faire par le biais de la réification. Il existe déjà des c.reifyTree y c.reifyType qui le font pour les arbres et les types, mais c.reifySymbol n'existe pas encore.

Heureusement, la réification des symboles devrait être très facile à mettre en œuvre. Il suffit d'envelopper votre symbole dans un Ident, c'est-à-dire d'écrire Ident(sym) et appeler ensuite c.reifyTree sur le résultat. Au moment de l'exécution, il suffit d'extraire un symbole de l'arbre d'enveloppement et le tour est joué.

P.S. Pour l'instant, je n'ai pas le temps d'écrire le code qui fait toutes ces choses. Si l'un d'entre vous implémente la macro et poste le code, j'enlèverai volontiers ma réponse et je vous donnerai un "upvote" :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X