2013-08-19 17 views
6

Wzdłuż linii this question, staram się znaleźć sposób, aby uzyskać kompilator Scala wywnioskować największy wspólny podtyp dwa typy A i B.Znajdź największy wspólny podtyp dwóch rodzajów Scala

Coś jak „ bez B”, gdzie definicja jest:

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

lub funkcję, która zwraca typ C, gdzie:

EDIT:

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

tj. A jest podtypem C i C nie jest podtypem B

W rzeczywistości oczekuję, że ktoś wskaże, że nie jest to ten sam "największy wspólny podtyp", ponieważ tak naprawdę nie wymaga tego A <: B.

Zastosowanie:

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 

Następnie chciałbym móc propagować ograniczenia typu, co następuje:

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] { 

Próbowałem wymyślić coś przy użyciu pewne ograniczenia typu pożyczone z innych odpowiedzi 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 

Mam kilka przypadków testowych:

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] 

Powinny one niepowodzeniem:

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

Nie jestem pewien, czy rozumiem twoją definicję. Brzmi to tak, jakby 'A <: B', a następnie' C' nie istnieje, w przeciwnym razie 'C' jest po prostu' A'. –

+0

@TravisBrown Na przykład: "C: POSINT", "B: CONST". Następnie 'A: C z B' = POSINTCONST. Według mojej pierwszej definicji, "POSINTCONST bez CONST =: = POSINT". Z drugiej strony 'A <:! RealName

+0

Właściwie @Travis Zrobiłem błąd w mojej drugiej definicji (mam teraz ustaloną nadzieję). – RealName

Odpowiedz

1

Dobrze po losowo walnąć klawiaturze dużo i czytając jak mogłem zrozumieć o ograniczeniach typu, to co mam wymyślić:

// 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] 
    } 
} 

test, który wydaje się działać:

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 ]] 

A nie kiedy powinien:

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

implicitly dostaje kompilatora t o szukać niejawnej funkcji w bieżącym niejawnym zasięgu, który może wytworzyć obiekt wymaganego typu (w tym przypadku instancja klasy jest bez). Jeśli znajdzie taki, który spełnia sygnaturę typu, to kompiluje (nie sądzę, aby miało znaczenie to, co zwraca metoda apply zdefiniowana w klasie). Ważnym punktem jest podpis typu, który korzysta z <:!< wspomnianego w pytaniu i pożyczonego od innej odpowiedzi SO Milesa.

Podpis ten typ mówi A oznacza podtyp C i C nie musi być podtyp B. Alternatywna wersja (odpowiadający pierwszej definicji pytaniu) to:

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 
} 

Może to być wykorzystywane na kilka sposobów:

  1. Aby sprawdzić ważność hierarchii typu (tzn testowych.), jak widać z powyższego wykorzystania implicitly.

  2. Aby określić rodzaj parametru Metoda:

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

    zauważyć, że ten wykorzystuje występ typu without#l[C] = isWithout[A, B, C] jako związku związanego którym desugars kompilator:

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

    zatem wymaga, aby określony niejawny był w zakresie.

  3. jako rodzaj przymusu, zgodnie z wnioskiem w oryginalnym pytanie:

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

    kompiluje ten, chociaż muszę przyznać, że nie skończył jeszcze niczego więc nie można robić to, co myślę, że to robi .

3

Brzmi jak chcesz kres górny (LUB) rodzajów Scala? Szukałbym inspiracji do bezkształtnej biblioteki Milesa, gdzie faktycznie mają LUBConstraint.

A jeśli chcesz mieć większy dolny limit (GLB), obawiam się, że musiałbym polecić ci użycie definicji makra, w której możesz uzyskać albo LUB, albo GLB, patrz Types.

+1

Lubs i glbs można również obliczać za pomocą refelctionów wykonawczych. –

+0

@wheaties Widziałem Shapeless i "zaglądałem" to, ale uważam, że jest całkiem nieprzenikniony. Wierzę, że GLB jest tym, czego chcę i ten link może być przydatny, jednak miałem nadzieję na bardziej szczegółową odpowiedź. – RealName

+0

@Eugene runtime refleksja byłaby dobra, ponieważ w końcu chcę móc konstruować nowe wyrażenia w czasie wykonywania. – RealName

Powiązane problemy