2016-05-27 6 views
2
Begrenzung

Es ist ein Programm, wo Ich mag würde den Bereich auf einer Reihe von Ints von 5 bis 15.Scala Typ für eine Int zu einer Reihe

Gibt es eine Möglichkeit zu begrenzen, eine Art, die dies ermöglicht definieren?

Ein Beispiel, wie möchte diese verwenden:

// Define type Good X as range from 5 to 15 

class Foo(val x: GoodX) 
{ 
    //blah blah 
} 

ich auch die „Int-iness“ von GoodX erhalten möchten.

val base:GoodX=5 
val f=Foo(base+4) 

Antwort

6

Werfen Sie einen Blick auf https://github.com/fthomas/refined. Sie können vorhandene Typen auf Typenebene verfeinern (einschränken). Z.B. positive ganze Zahlen, die immer noch eine Untertypbeziehung mit ganzen Zahlen haben.

Die Syntax ist ein wenig ausführlich, und es wird Box-Primitiven (siehe unten für Details). Aber ansonsten macht es genau das, was du willst.

Hier ist eine kurze Demo. Definieren Sie eine Verfeinerung und ein Verfahren eine raffinierte Art mit:

import eu.timepit.refined._ 
import eu.timepit.refined.api.Refined 
import eu.timepit.refined.auto._ 
import eu.timepit.refined.numeric._ 

type FiveToFifteen = GreaterEqual[W.`5`.T] And Less[W.`15`.T] 
type IntFiveToFifteen = Int Refined FiveToFifteen 

def sum(a: IntFiveToFifteen, b: IntFiveToFifteen): Int = a + b 

Verwenden Sie es mit Konstanten (man beachte die gute Kompilierung Fehlermeldungen):

scala> sum(5,5) 
res6: Int = 10 

scala> sum(0,10) 
<console>:60: error: Left predicate of (!(0 < 5) && (0 < 15)) failed: Predicate (0 < 5) did not fail. 
     sum(0,10) 
     ^

scala> sum(5,20) 
<console>:60: error: Right predicate of (!(20 < 5) && (20 < 15)) failed: Predicate failed: (20 < 15). 
     sum(5,20) 
      ^

Wenn Sie Variablen haben, wissen Sie nicht, bei der Kompilierung ob sie in Reichweite sind oder nicht. So kann das Downcasting von Int zu einem verfeinerten Int fehlschlagen. Das Auslösen von Ausnahmen wird in funktionalen Bibliotheken nicht als guter Stil angesehen. So ist die refineV Methode gibt ein Entweder:

val x = 20 
val y = 5 

scala> refineV[FiveToFifteen](x) 
res14: Either[String,eu.timepit.refined.api.Refined[Int,FiveToFifteen]] = Left(Right predicate of (!(20 < 5) && (20 < 15)) failed: Predicate failed: (20 < 15).) 

scala> refineV[FiveToFifteen](y) 
res16: Either[String,eu.timepit.refined.api.Refined[Int,FiveToFifteen]] = Right(5) 
+0

Rüdiger, könnten Sie sich bitte diese Frage ansehen? http://stackoverflow.com/q/37944150/226895 – expert

-1

Sicher ...

object FiveToFifteen extends Enumeration { 
val _5 = Value(5) 
val _6,_7,_8,_9,_10,_11,_12,_13,_14,_15 = Value 
} 

bearbeiten, wenn Sie auf "int-ness bewahren" wollen, können Sie auch Konvertierungen wie folgt hinzu:

implicit def toInt(v: Value) = v.id 
implicit def fromInt(i: Int) = apply(i) 

Aber das, offensichtlich, wird nicht Ihren Typ viel mehr "int-ful" dann ist es bereits (was ist, so ziemlich keiner), weil Dinge wie val v: Value = _15 - _10 oder val v: Value = _5 * 3 oder sogar val v = _15 * _5 funktioniert, aber andere, wie val v: Value = _5 - 1 wird crash

+0

Ich dachte über Enumeration nach, aber ich denke nicht, dass es die "Int-ininess" des Bereichs beibehält. – TLOlczyk

+0

Es gibt nichts "int-ful" über einen Kardinaltyp, der nur für 11 verschiedene Werte definiert ist. – Dima

0

Ich denke Partial Function helfen würde.

case class GoodX(x: Int) 

object GoodX { 
    def apply: PartialFunction[Int, GoodX] = 
    { case i if i > 5 && i < 15 => new GoodX(i) } 
} 

// implicits to remain int-fulness 
implicit def goodXToInt(goodX: GoodX): Int = goodX.x 

GoodX(5) // throw Match Error 
GoodX(10) // GoodX(10) 

Diese Lösung erfordert keine Bibliothek. Hoffe diese Hilfe.