2013-07-26 9 views
6

che voglio implementare il metodo concatenamento come in queste domande:Tipo sicuro metodo di concatenamento che non consente repliche di operazioni

Best practice to implement Scala trait which supports method chaining;

Scala DSL: method chaining with parameterless methods

Tuttavia, voglio che una volta che una "proprietà" è stato utilizzato, non può più essere utilizzato. Ad esempio, supponiamo di avere una classe "Myclass" per la quale voglio consentire l'uso della definizione "foo" e la definizione "bar" al massimo una volta e non mi interessa il tipo di ritorno finale. Così:

val c = new Myclass 
c foo //ok ! 
c foo bar // ok! 
c foo foo // refuse to compile 
c foo bar foo //refuse to compile 

sto lottando con questo problema per un po 'e la mia visione comincia a diventare sfocata! Ho provato a utilizzare le classi implicite, tuttavia, se hanno bisogno di analizzare gli oggetti che non hanno utilizzato la proprietà associata, e non riesco a trovare come, se hanno bisogno di "consumare" la proprietà rimuovendola dall'oggetto disponibile proprietà, e, ancora, non riesco a trovare come.

Sono attualmente alla ricerca nella API di riflessione, ma è ancora un po 'oscura per me.

L'aiuto sarebbe apprezzato! =)

risposta

14

Vedere Phantom Types In Haskell and Scala da James Iry.

Si potrebbe anche usare type-safe builder:

trait TTrue 
trait TFalse 

@annotation.implicitNotFound(msg = "Cannot call same method twice.") 
sealed abstract class =^=[From, To] 
object =^= { 
    private val singleton_=^= = new =^=[Any, Any]{} 
    implicit def tpEquals[A]: A =^= A = singleton_=^=.asInstanceOf[A =^= A] 
} 

class Myclass[TFoo, TBar, TBuz] private(){ 
    def foo(implicit e: TFoo =^= TFalse) = new Myclass[TTrue, TBar, TBuz] 
    def bar(implicit e: TBar =^= TFalse) = new Myclass[TFoo, TTrue, TBuz] 
    def buz(implicit e: TBuz =^= TFalse) = new Myclass[TFoo, TBar, TTrue] 
} 

object Myclass{ 
    def apply() = new Myclass[TFalse, TFalse, TFalse] 
} 

da utilizzare come questo

scala> Myclass().foo.bar.buz 
res0: Myclass[TTrue,TTrue,TTrue] = [email protected] 

scala> Myclass().bar.buz.foo 
res1: Myclass[TTrue,TTrue,TTrue] = [email protected] 

scala> Myclass().foo.buz.foo 
<console>:12: error: Cannot call same method twice. 
       Myclass().foo.buz.foo 
           ^
+0

+1 per Type Builders. Ma questa implementazione è strettamente legata al numero di metodi, come aggiungere 10 nuovi metodi? E un tale errore nella produzione sarebbe strano – 4lex1v

+0

@AlexIv: per 10 metodi dovrai aggiungere 10 parametri di tipo. Messaggio di errore risolto È un errore di compilazione, non un errore di produzione runtime. – senia

+1

Grazie per la risposta. Ho capito l'idea principale: hai un flag TTrue/TFalse per ogni opzione che cambi quando l'opzione è attivata. Per rilevare l'attivazione di un'opzione al momento della compilazione, si richiede l'istanza implicita di un oggetto, oggetto che può essere implicitamente installato se e solo se la bandiera ha il valore corretto. Quindi, se l'opzione è stata chiamata, il flag è true, l'istanza implicita non è possibile e il compilatore ha fallito lanciando il test in annotazione. Penso che sia una soluzione molto elegante. –

Problemi correlati