2015-09-16 14 views
5

Dato il seguente AST per Success e Failure:vincoli ai Tipo firme per Sinistra e Destra

sealed trait Success 
case object FooGood extends Success 
case object BarGood extends Success 

sealed trait Failure 
case object FooBad extends Failure 
case object BarBad extends Failure 

E il metodo di firma:

def go[A <: Failure, B <: Success](x: Int): Either[A, B] = ??? 

Tuttavia, mi piacerebbe per vincolare l'Left e Right i tipi devono essere specifici per Foo o Bar.

Ma le seguenti compila codice (contro la mia volontà):

scala> go[FooBad.type, BarGood.type](5) 
scala.NotImplementedError: an implementation is missing 

Come posso ottenere questo vincolo a tempo di compilazione?

+1

Perché non utilizzare tratto Foo e Bar, con questa definizione: def andare [F] (x: Int): O [Fallimento con F, successo con F] = ??? – volia17

+0

Sono d'accordo che dovrebbe essere compilato. Non stavo affermando che dovrebbe ** non ** compilare. La mia domanda è come scrivere un'implementazione 'go' (in un modo generico) che non riuscirà a compilare se una' Barra 'non appare su entrambi i lati, cioè 'Destra' e' Sinistra', o viceversa per ' foo'. –

risposta

-1

Se c'è un comune super-tipo si desidera vincolare sia A & B a, è possibile sfruttare Scala di Compound Types come segue:

def go[F, A <: Failure with F, B <: Success with F](x: Int): Either[A, B] = ??? 

Dove F è il super-natura è comune a entrambi A e B .

+0

def go [F, A <: Fallimento con F, B <: successo con F] (x: Int): O [A, B] = ???, se vuoi andare più generico – volia17

+0

Perché i downvotes? – Chris

+0

@ volia17 d'accordo, questa è una firma molto più bella – Chris

2

Il problema è che il compilatore non sa che FooGood è in qualche modo correlato a FooBad, quindi è necessario suggerirlo in qualche modo.

Ecco cosa mi è venuta, anche se ammetto che non è molto elegante:

trait Grouping[B, G] 

object FooHelper { 
    implicit object fooGrouping Grouping[FooBad.type, FooGood.type] 
} 

object BarHelper { 
    implicit object barGrouping Grouping[BarBad.type, BarGood.type] 
} 

def go[A <: Failure, B <: Success](x: Int)(implicit ev: Grouping[A, B]): Either[A, B] = ??? 


import FooHelper._ 
import BarHelper._ 

// the following two type check 
go[FooBad.type, FooGood.type](5) 
go[BarBad.type, BarGood.type](5) 

// while these two do not 
go[FooBad.type, BarGood.type](5) 
go[BarBad.type, FooGood.type](5) 

Come si può vedere il suggerimento è implementato con la creazione di un Grouping e mettendo corrette raggruppamenti nel campo di applicazione implicita. Il problema con questo approccio è che l'utente potrebbe creare i propri raggruppamenti che potrebbero non essere validi.

0

E 'possibile limitare il tipo di output di essere coppia di Success/Failure introducendo il parametro type:

sealed trait Success[T] 
sealed trait Failure[T] 

sealed trait Foo 
sealed trait Bar 

case object FooGood extends Success[Foo] 
case object BarGood extends Success[Bar] 

case object FooBad extends Failure[Foo] 
case object BarBad extends Failure[Bar] 

def go[T, A <: Failure[T], B <: Success[T]](x: Int): Either[A, B] = ??? 

Ma! Questo ha senso solo se creerai delle implementazioni concrete che forniscano il tipo T, perché in altro modo non ha senso creare tale firma.

vedo alcuna utilità per questo come una parte di interfaccia, che vincola frutto di go, in questo modo:

trait Paired[T] {     
    type A <: Failure[T]    
    type B <: Success[T]    
    def go(x: Int): Either[A, B]  
} 

Se si desidera fornire i tipi in modo esplicito (ma come farà a creare implementazione per questo?) - rispondere fornendo gruppi impliciti è molto ragionevole.

+0

Aggiunta: 'tratto sigillato FooBarParent' (che' Foo' e 'Bar' si estendono), e quindi:' def go [T <: FooBarParent, ... resto del codice ... 'aggiungerebbe quel vincolo? –

+1

@KevinMeredith Logicamente consumerebbe quindi qualsiasi 'FooBarParent'. A proposito, se dovessi spiegare nella domanda come penserai di usare il tuo codice, potrebbe portare le persone a proposte migliori. Anche se ti capita di dichiarare qualche firma che ti soddisfi in un unico metodo, non puoi dargli alcuna implementazione concreta, eccetto '??? 'o altre eccezioni. Qual è il caso d'uso? – dmitry

4

Ecco una soluzione che si basa su una classe di tipo. Da notare che non è necessario definire manualmente nuove istanze di classi di tipi per ciascun nodo (coppia di) AST. Si tratta di introdurre un super tipo comune per ogni coppia (sebbene non sia tecnicamente avere per usarlo come classe base, è semplicemente usato come tipo di tag).

sealed trait Success[T] 
abstract sealed class Foo 
abstract sealed class Bar 
case object FooGood extends Foo with Success[Foo] 
case object BarGood extends Bar with Success[Bar] 
sealed trait Failure[T] 
case object FooBad extends Foo with Failure[Foo] 
case object BarBad extends Bar with Failure[Bar] 

@annotation.implicitNotFound("Expecting reciprocal Failure and Success alternatives, but got ${A} and ${B}") 
trait IsGoodAndBadFacet[A,B] 
implicit def isGoodAndBadFacet[T,A,B](implicit e1: A <:< Failure[T], e2: B<:<Success[T]): IsGoodAndBadFacet[A,B] = null 

def go[A, B](x: Int)(implicit e: IsGoodAndBadFacet[A,B]): Either[A, B] = ??? 

Repl prova:

scala> go[FooBad.type, BarGood.type](5) 
<console>:17: error: Expecting reciprocal Failure and Success alternatives, but got FooBad.type and BarGood.type 
       go[FooBad.type, BarGood.type](5) 
             ^

scala> go[FooBad.type, FooGood.type](5) 
scala.NotImplementedError: an implementation is missing 
    at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225) 
    at .go(<console>:11) 
    ... 33 elided 

scala> go[BarBad.type, BarGood.type](5) 
scala.NotImplementedError: an implementation is missing 
    at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225) 
    at .go(<console>:11) 
    ... 33 elided 
Problemi correlati