2017-09-25 2 views
0

Dans un effort pour apprendre le système de macro Scala, je pensais que je vais essayer ma main à l'écriture d'une macro de transformation CPS de base. J'ai déjà écrit un cadre de transformation CPS assez complet pour Clojure, donc je suis assez familier avec la transformation CPS elle-même. Cependant, je suis coincé transformant des applications de fonction/méthode.Comment utiliser l'identificateur frais dans la liste des paramètres de fonction Scala macro

Pour la CPS transform, les appels de fonction de la forme suivante:

cps(f(<a>, <b>, <c>, ...)) 

doivent se traduire par une expression de la forme:

cps(<a>){ $a => 
    cps(<b>){ $b => 
    cps(<c>){ $c => 
     ... => f($a, $b, $c, ...) 
    } 
    } 
} 

De toute évidence, les paramètres de la poursuite générés lambdas (par exemple, $a) doivent être des symboles nouveaux afin qu'ils ne puissent pas entrer en conflit par inadvertance avec les noms de variables dans le contexte lexical. Donc, pour chaque argument arg, je produis un nom frais:

val name = Ident(TermName(c.freshName)) 

(où c est la de Context macro) que je l'utilise ensuite dans le quasiquote suivant:

q"""cps(arg)($name => $remainder)""" 

remainder fait référence au reste de le calcul.

La macro se compile bien, mais lorsque je tente de l'utiliser avec une expression qui implique une application de fonction, je reçois l'erreur suivante:

... exception during macro expansion: 
[error] java.lang.IllegalArgumentException: fresh$macro$1 is not valid representation of a parameter, consider reformatting it into q"val $name: $T = $default" shape 

Cependant, je ne pense pas qu'il est possible d'effectuer le "reformatage" recommandé, car il n'y a pas de $default à fournir.

Voici un exemple minimal qui illustre la question que je vais avoir:

def id[A](expr : A) : A = macro idImpl[A] 

def idImpl[A](c : blackbox.Context)(expr : c.Expr[A]) : c.Expr[A] = { 
    import c.universe._ 

    val name = Ident(TermName(c.freshName)) 

    //c.Expr(q"""val $name = $expr; $name""") 
    c.Expr(q"""($name => $name)($expr)""") 
} 

Notez que cela fonctionne si vous remplacez l'expression lambda avec la ligne commenté.

Ma question: Comment un nom généré par freshName être utilisé comme nom de paramètre pour une fonction anonyme?

+0

https://github.com/ReactiveMongo/ReactiveMongo/blob/master/macros/src/main/scala-2.12/MacroImpl.scala#L101 – cchantep

+0

@cchantep, souhaitez-vous élaborer? Il ne semble pas y avoir de fonction définie par la macro là-bas. –

+0

Si vous lisez attentivement, vous pouvez voir 'termName (..)' – cchantep

Répondre

-1

Je crois que vous avez besoin d'un ValDef de la forme val $name: $T = _, où le _ est représenté par NoTree. Initialisation des variables avec _ est Scala valide, la mise en référence à null et les numéros à 0 (à savoir, les valeurs par défaut), mais est également utilisé comme un espace réservé interne lorsqu'il y a ValDef s (par exemple des paramètres de la fonction), mais aucun défaut sain.

Je suis sur mobile ici, donc je ne peux pas tester. Dans tous les cas, vous pouvez déstructurer la valeur q"a: Any => a" dans le REPL pour trouver ce que le compilateur définit normalement comme valeur par défaut, si NoTree ne fonctionne pas.

+0

Merci, je ne savais pas à propos de '_' comme valeur par défaut. Cependant, la question est maintenant de savoir comment déterminer «T». Dans mon exemple artificiel, il est au moins théoriquement simple (bien que les détails pour le réaliser soient pour l'instant schématiques). Mais dans le cas de CPS, je suppose que je devrais inférer le type de l'arbre pour chaque argument? –