2012-12-02 5 views
38

Così ho questa macro:statico tipo di ritorno di Scala macro

import language.experimental.macros 
import scala.reflect.macros.Context 

class Foo 
class Bar extends Foo { def launchMissiles = "launching" } 

object FooExample { 
    def foo: Foo = macro foo_impl 
    def foo_impl(c: Context): c.Expr[Foo] = 
    c.Expr[Foo](c.universe.reify(new Bar).tree) 
} 

ho detto tre volte che voglio foo per restituire un Foo, eppure posso fare quanto segue (in 2.10. 0-RC3):

scala> FooExample.foo 
res0: Bar = [email protected] 

scala> res0.launchMissiles 
res1: String = launching 

La stessa cosa succede se rimuovere i parametri di tipo su entrambi c.Expr. Se voglio davvero assicurarmi che chiunque chiami il numero foo non riesca a vedere che stanno ottenendo un Bar, devo aggiungere un attributo di tipo nell'albero stesso.

questo è in realtà abbastanza grande, ciò significa ad esempio che posso puntare una macro a uno schema di qualche tipo e creare una sottoclasse anonima di qualche Vocabulary classe con metodi membri che rappresentano i termini del vocabolario, e questi saranno disponibili sul l'oggetto restituito.

Mi piacerebbe capire esattamente quello che sto facendo, però, quindi ho un paio di domande. Innanzitutto, qual è il tipo di ritorno per il metodo foo? È solo disponibile per la documentazione (facoltativa)? Esso vincola chiaramente il tipo di ritorno (ad esempio, non posso cambiare a Int in questo caso), e se tolgo del tutto ottengo un errore come questo:

scala> FooExample.foo 
<console>:8: error: type mismatch; 
found : Bar 
required: Nothing 
       FooExample.foo 
         ^

Ma io posso cambiarlo Any e ricevo ancora un valore statico Bar quando chiamo foo.

In secondo luogo, questo comportamento è specificato da qualche parte? Questo mi sembra un insieme abbastanza elementare di problemi, ma non sono stato in grado di cercare una spiegazione o una discussione chiara.

+2

@ som-snytt: Ma mi sarei comunque aspettato che il tipo restituito su "pippo" avesse l'ultima parola (anche se sono anche contento che non lo sia). –

+2

L'annotazione del tipo di ritorno su 'FooExample.foo' è molto bizzarra qui. Questo è altrimenti come mi aspetterei che i macro si comportino. – drstevens

+0

@drstevens: concordato. –

risposta

19

Questo comportamento è sottodescritto ma inteso, sebbene possa sembrare confuso. Abbiamo in programma di elaborare il ruolo del tipo restituito nelle macro firme, ma al momento sento che la flessibilità è una buona cosa da avere.

Anche a volte il comportamento è incoerente, ad es. quando la macro viene catturata durante l'inferenza del tipo, verrà utilizzata la sua firma statica (ad esempio, Foo nell'esempio), non il tipo dell'espansione effettiva. Questo perché l'espansione delle macro è intenzionalmente ritardata fino a quando non viene eseguita l'inferenza di tipo (in modo che le implementazioni macro possano vedere i tipi dedotti, non i tipi vars). Questo è un trade-off e non è necessariamente il migliore, quindi abbiamo intenzione di rivederlo presto: https://issues.scala-lang.org/browse/SI-6755.

Un altro problema in questo reparto è con macro implicite. Quando il tipo restituito di una macro implicita è generico e deve essere dedotto dal tipo richiesto di un valore implicito, accadono cose brutte. Ciò rende attualmente impossibile l'utilizzo di macro per generare tag di tipo: https://issues.scala-lang.org/browse/SI-5923.

+0

Ciò significa che non è possibile utilizzare il tipo di espansione effettiva nella ricerca implicita a meno che la ricerca non sia effettuata esplicitamente dalla macro stessa? –

+1

Non possibile al momento e probabilmente non sarà possibile in seguito. Altrimenti la ricerca implicita dovrebbe espandere con entusiasmo tutte le macro implicite nell'ambito. Hai in mente un particolare caso d'uso? –

+0

No, sto solo cercando di creare un'immagine mentale su come funzionano le cose; c.inferImplicitValue farà il trucco nella maggior parte dei casi, penso. –