2015-09-16 6 views
5

folgende AST für Success und Failure Gegeben:Erzwinge Typ Signaturen für Links und Rechts

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 

Und die Methode Unterschrift:

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

Allerdings würde Ich mag die Left und Right beschränken Spezifische Typen für Foo oder Bar.

Aber die folgenden Code kompiliert (gegen meinen Willen):

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

Wie kann ich diese Einschränkung bei der Kompilierung-Zeit erreichen?

+1

Warum nicht Charakterzug Foo und Bar verwendet wird, mit dieser Definition: def go [F] (x: Int): Entweder [Failure mit F, Erfolg mit F] = ??? – volia17

+0

Ich stimme zu, dass es kompilieren sollte. Ich behauptete nicht, dass es ** nicht ** kompilieren sollte. Meine Frage ist, wie man eine "Go" -Implementierung (auf generische Weise) schreibt, die nicht kompiliert werden kann, wenn nicht auf beiden Seiten ein "Balken" erscheint, dh "rechts" und "links" oder umgekehrt Foo. –

Antwort

-1

Wenn es einen gemeinsamen Super-Typ ist, dass Sie beide A & B einschränken möchten, können Sie Scala nutzen können Compound Types wie folgt:

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

Wo F ist die gemeinsame Super-Typ sowohl A und B .

+0

def gehen [F, A <: Fehler mit F, B <: Erfolg mit F] (x: Int): Entweder [A, B] = ???, wenn Sie generische gehen möchten – volia17

+0

Warum die downvotes? – Chris

+0

@ volia17 einverstanden, das ist eine viel schönere Signatur – Chris

2

Das Problem, das Sie haben, ist, dass der Compiler nicht weiß, dass FooGood irgendwie mit FooBad verwandt ist, also müssen Sie es irgendwie andeuten.

Hier ist, was ich kam mit, obwohl ich es nicht sehr elegant zugeben:

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) 

Wie Sie der Hinweis sehen, indem Sie eine Grouping und setzen korrekte Gruppierungen in den impliziten Umfang umgesetzt wird. Das Problem bei diesem Ansatz besteht darin, dass der Benutzer möglicherweise eigene Gruppierungen erstellt, die möglicherweise nicht gültig sind.

0

Es ist möglich, Ausgangstyp sein Paar Success/Failure durch die Einführung der Typ-Parameter zu beschränken:

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

Aber! Dies ist nur sinnvoll, wenn Sie eine konkrete Implementierung erstellen, die den Typ T bereitstellt, da es sonst sinnlos ist, eine solche Signatur zu erstellen.

ich jeden Nutzen für diese als Teil der Schnittstelle sehen, die aufgrund der go, wie diese beschränkt:

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

Wenn Sie explizit Typen liefern (aber wie werden Sie Implementierung für diese erstellen?) - Antwort mit impliziten Gruppierung ist sehr sinnvoll.

+0

Hinzufügen: 'versiegelte Eigenschaft FooBarParent' (welche' Foo' und 'Bar' erweitern), und dann:' def go [T <: FooBarParent, ... Rest deines Codes ... 'würde diese Einschränkung hinzufügen? –

+1

@KevinMeredith Logischerweise würde es dann jeden 'FooBarParent' verbrauchen. Übrigens, wenn Sie in der Frage erklären würden, wie Sie Ihren Code verwenden möchten, könnte dies zu besseren Vorschlägen führen. Selbst wenn Sie irgendeine Signatur deklarieren, die Sie in einer einzigen Methode zufrieden stellt, können Sie keine konkrete Implementierung angeben, außer '???' oder eine andere Ausnahme. Was ist der Anwendungsfall? – dmitry

4

Hier ist eine Lösung, die auf einer Typklasse beruht. Es ist zu beachten, dass es nicht erforderlich ist, neue Typklasseninstanzen für jeden (Paar) AST-Knoten manuell zu definieren. Es beinhaltet die Einführung einer gemeinsamen Super-Typ für jedes Paar (obwohl Sie technisch haben, um es als Basisklasse zu verwenden, wird es nur als Tag-Typ verwendet).

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 Test:

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 
Verwandte Themen