2013-08-19 16 views
6

Lungo le linee di this question, sto cercando di trovare un modo per ottenere il compilatore Scala di dedurre il massimo sottotipo comune di due tipi A e B.Trova più grande sottotipo comune di due tipi Scala

Qualcosa come "A senza B", in cui la definizione è:

(A without B = C) === (A = C with B) 

o una funzione che restituisce tipo C, dove:

EDIT:

A <: C && C <:!< B 

ie. A è un sottotipo di C e C non è un sottotipo di B

In effetti, mi aspetto che qualcuno indichi che questo non è lo stesso del "più grande sottotipo comune", dal momento che in realtà non è necessario che sia A <: B.

Usage:

trait Syntax 

trait ANYSYNTAX extends Syntax 
trait NUMERIC extends ANYSYNTAX 
trait DISCRETE extends ANYSYNTAX 
trait POSITIVE extends ANYSYNTAX 
trait CONST extends ANYSYNTAX  
type NUMCONST = NUMERIC with CONST 
type POSCONST = POSITIVE with CONST 
type ORDINALCONST = DISCRETE with CONST 
type INTEGER = NUMERIC with DISCRETE 
type POSNUM = POSITIVE with NUMERIC 
type POSINT = POSNUM with INTEGER 
type INTCONST = INTEGER with NUMCONST with ORDINALCONST 
type POSNUMCONST = POSNUM with POSCONST with NUMCONST 
type POSINTCONST = POSNUMCONST with INTCONST with POSINT 

Poi vorrei essere in grado di propagare vincoli di tipo, come segue:

abstract class Expression[+R](val args: Expression[_]*) 

case class Add[A <: R, R <: NUMERIC](arg1: Expression[A], arg2: Expression[A]) extends Expression[R] { 
case class Subtract[A <: R, R : A without POSITIVE](arg1: Expression[A], arg2: Expression[A]) extends Expression[R] { 
case class Multiply[A <: R, R <: NUMERIC](arg1: Expression[A], arg2: Expression[A]) extends Expression[R]{ 
case class Divide[A <: R, R : A without DISCRETE](arg1: Expression[A], arg2: Expression[A]) extends Expression[R] { 

Ho cercato di trovare qualcosa usando alcuni vincoli di tipo presi in prestito da altre risposte SO:

sealed class =!=[A,B] 

trait LowerPriorityImplicits { 
    implicit def equal[A]: =!=[A, A] = sys.error("should not be called") 
} 
object =!= extends LowerPriorityImplicits { 
    implicit def nequal[A,B](implicit same: A =:= B = null): =!=[A,B] = 
    if (same != null) sys.error("should not be called explicitly with same type") 
    else new =!=[A,B] 
} 

// Encoding for "A is not a subtype of B" 
trait <:!<[A, B] 

// Uses ambiguity to rule out the cases we're trying to exclude 
implicit def nsub[A, B] : A <:!< B = null 
implicit def nsubAmbig1[A, B >: A] : A <:!< B = null 
implicit def nsubAmbig2[A, B >: A] : A <:!< B = null 

ho alcuni casi di test:

implicitly[POSINT <:!< CONST] 
implicitly[POSITIVE <:!< OPINION] 
implicitly[DOGMA <:!< CONST] 

implicitly[POSINTCONST <:< POSITIVE with CONST] 
implicitly[POSINTCONST <:< POSCONST] 
implicitly[POSITIVE with CONST <:!< POSINTCONST] 

implicitly[POSITIVE =:= POSCONST without CONST] 
implicitly[NUMERIC =:= INTEGER without DISCRETE] 
implicitly[POSINT =:= POSINTCONST without CONST] 

Questi dovrebbero fallire:

implicitly[POSINT =:= POSINTCONST without OPINION] 
implicitly[POSINT with OPINION =!= POSINTCONST without OPINION] 
+0

Non sono sicuro di aver capito la tua definizione. Sembra che se A <: B', quindi 'C' non esiste, e altrimenti' C' è solo 'A'. –

+0

@TravisBrown Ad esempio, 'C: POSINT',' B: CONST'. Quindi 'A: C con B' = POSINTCONST. Secondo la mia prima definizione quindi, 'POSINTCONST senza CONST =: = POSINT'. Se d'altra parte 'A <:! RealName

+0

In realtà @Travis ho fatto un errore nella mia seconda definizione (ora fisso, spero). – RealName

risposta

1

Bene dopo casualmente colpire la tastiera molto e la lettura di quanto ho potuto capire sui vincoli di tipo, questo è quello che sono venuto con:

// A without B is C 
sealed abstract class isWithout[A, B, C] 

object Syntax { 

    implicit def composedWithout[A <: C, B, C](implicit ev: C <:!< B): isWithout[A, B, C] = new isWithout[A, B, C] { 
    def apply(a: A) = a 
    } 

    type without[A, B] = { 
    type l[C] = isWithout[A, B, C] 
    } 
} 

prova che sembra funzionare:

implicitly[isWithout[POSCONST, POSITIVE, CONST]] 
implicitly[isWithout[POSINTCONST, DISCRETE, POSNUMCONST]] 
implicitly[isWithout[POSINTCONST, DISCRETE, POSITIVE]] 
implicitly[isWithout[POSNUM, CONST, POSNUM]] 
implicitly[isWithout[POSCONST, CONST, POSITIVE ]] 
implicitly[isWithout[POSCONST, POSITIVE, CONST ]] 
implicitly[isWithout[INTEGER, DISCRETE, NUMERIC ]] 
implicitly[isWithout[POSINTCONST, CONST, POSINT ]] 

e fallisce quando dovrebbe:

implicitly[isWithout[POSINTCONST, INTCONST, POSINTCONST]] 
implicitly[isWithout[NUMERIC, ANYSYNTAX, ANYSYNTAX]] 
implicitly[isWithout[INTEGER, POSITIVE, POSINT]] 
implicitly[isWithout[POSNUM, DISCRETE, POSCONST]] 

implicitly ottiene il compilatore t o cercare una funzione implicita nell'attuale ambito implicito che può produrre un oggetto del tipo richiesto (in questo caso, un'istanza della classe isWithout). Se ne trova uno che soddisfa la firma del tipo, allora lo compila (non penso che importi ciò che il metodo apply definito nella classe restituisce). Il punto importante è la firma del tipo, che fa uso di <:!< menzionato nella domanda e preso in prestito da un'altra risposta SO di Miles.

Questa firma tipo dice: A è un sottotipo di C e C non deve essere un sottotipo di B. Una versione alternativa (corrispondente alla prima definizione in questione) sarebbe:

implicit def composedWithout[A <: C with B, B, C](implicit ev: C <:!< B): isWithout[A, B, C] = new isWithout[A, B, C] { 
    def apply(a: A, b: B) = a 
} 

Questo può essere utilizzato in vari modi:

  1. Per verificare la validità del tipo gerarchia (cioè casi di test.), come illustrato con l'uso sopra di implicitly.

  2. Per specificare il tipo di un parametro di metodo:

    def test[R : without[POSINTCONST, DISCRETE]#l](v: R) = v

    notare che utilizzando la sporgenza tipo without#l[C] = isWithout[A, B, C] come contesto vincolato che i desugars compilatore a:

    def test[R](v: R)(implicit $ev0: isWithout[POSINTCONST, DISCRETE, R]) = v 
    

    Così richiede che l'implicito specificato sia incluso nell'ambito.

  3. come un vincolo di tipo, come richiesto nella domanda iniziale:

    case class Subtract[A <: R, R <: A without POSITIVE](arg1: Expression[A], arg2: Expression[A]) extends BinaryPrimitive[A, R](arg1, arg2)

    Questo compila, anche se ammetto che non ho eseguito ancora nulla di così non potrebbe fare quello che io penso che sta facendo .

3

Sembra che si desidera un estremo superiore (LUB) dei tipi di Scala? Guarderei la libreria Shapeless di Miles per l'ispirazione, dove hanno in realtà uno LUBConstraint.

O se si desidera il Lower Lower Bound (GLB), temo di dover fare riferimento all'utilizzo di una definizione di macro in cui è possibile ottenere sia il LUB che il GLB, vedere Types.

+1

Lubs e glbs possono essere calcolati anche con il refelction di runtime. –

+0

@wheaties Ho visto Shapeless e l'ho guardato, ma lo trovo piuttosto impenetrabile. Credo che GLB sia quello che voglio e quel collegamento potrebbe essere utile, tuttavia speravo in una risposta più dettagliata. – RealName

+0

@Eugene riflessione runtime sarebbe buona poiché, alla fine, voglio essere in grado di costruire nuove espressioni in fase di esecuzione. – RealName

Problemi correlati