2012-05-13 8 views
6

Tengo la siguiente clase genérica Intervalo (amablemente formulado para mí por soc usuario):Scala implícita numérico [T] en el objeto compañero

case class Interval[T](from: T, to: T)(implicit num: Numeric[T]) { 
    import num.mkNumericOps // allows us to write from.toDouble and to.toDouble 
    def mid: Double = (from.toDouble + to.toDouble)/2.0 
} 

casos de uso típicos: intervalo [Doble] o intervalo [Int]. Para añadir binaria la unión y la intersección operadores he seguido un patrón similar con (implicit num: Numeric[T]) en el objeto acompañante:

object Interval { 

    def union[T](interval1: Interval[T], interval2: Interval[T])(implicit num: Numeric[T]) = { 
    import num.mkOrderingOps // allows interval1.from min 
    Interval[T](interval1.from min interval2.from, interval1.to max interval2.to) 
    } 

    def intersect[T](interval1: Interval[T], interval2: Interval[T])(implicit num: Numeric[T]) = { 
    import num.mkOrderingOps 
    Interval[T](interval1.from max interval2.from, interval1.to min interval2.to) 
    } 

} 

Es feo texto modelo para copiar el (implicit num: Numeric[T]) y import num.mkOrderingOps dentro de los dos métodos. ¿Hay alguna manera de hacer esto solo una vez, al nivel del objeto Interval en sí?

Respuesta

8

Sí, sí.

Primero con la importación. En su lugar, puede importar Ordering.Implicits._ en el ámbito de Interval.

object Interval { 
    import Ordering.Implicits._ 

    def union[T](....)(implicit num: Numeric[T]) = { 
    // do not import num.mkOrderingOps 
    ... 
    } 
    ... 
} 

Con esos implícitos, cuando se encuentre una operación de ordenación, se buscará el orden implícito (Numérico es una Orden) en el ámbito donde sucede la operación. Y sucede que hay un alcance propiamente implícito en cada una de sus rutinas. Si necesita operaciones aritméticas también, también importe Numeric.Implicits._

Ahora con el argumento implícito.Hay un acceso directo en el idioma para eso, lo que se llama un contexto ligado: puede escribir def f[T: X](args) en lugar de def f[T](args)(implicit someName: X[T])

La diferencia es que usted no tiene un nombre para la implícita con el contexto ligado (se puede utilizar implícitamente [T] pero luego esto no es más corto. Afortunadamente, no es necesario un nombre más con la importación Ordering.Implicits._

Así

object Interval { 
    import Ordering.Implicits._ 
    // also import Numeric.Implicits._ if you need +,-,*,/ ... 
    def union[T: Numeric] ... 
    def intersection[T: Numeric] ... 
} 
+0

Gracias. Buena brevedad. Nunca he visto esta idea ligada al contexto. ¿Dónde uno aprende tales cosas? Leí un libro sobre Scala, pero no recuerdo los límites del contexto. –

+0

Ver por ejemplo [¿Qué es un "contexto vinculado" en Scala?] (Http://stackoverflow.com/questions/2982276/what-is-a-context-bound-in-scala). – Jesper

5

Los usos de la clase de tipo Numeric en el objeto Interval tienen un parámetro de tipo T que debe vincularse en algún lugar de su ámbito adjunto. Interval, al ser un valor constante único, no puede proporcionar ese enlace.

Una solución a este problema específico sería mover las definiciones de sus union y intersect operaciones en las Intervalclase métodos de instancia como ordinarias, en cuyo caso que compartirían la unión de T y el asociado Numeric instancia con el resto de la clase,

case class Interval[T : Numeric](from: T, to: T) { 
    import Numeric.Implicits._ 
    import Ordering.Implicits._ 

    def mid: Double = (from.toDouble + to.toDouble)/2.0 
    def union(interval2: Interval[T]) = 
    Interval(this.from min interval2.from, this.to max interval2.to) 
    def intersect(interval2: Interval[T]) = 
    Interval(this.from max interval2.from, this.to min interval2.to) 
} 

Si, sin embargo, prefiere mantener las definiciones de estas operaciones separar del Interval clase, un enfoque para reducir la cantidad de texto modelo implícito que es necesario perseguir throug h sus API es definir sus propias clases de tipo de nivel superior en términos de Numérico [T]. Por ejemplo,

// Type class supplying union and intersection operations for values 
// of type Interval[T] 
class IntervalOps[T : Numeric] { 
    import Ordering.Implicits._ 

    def union(interval1: Interval[T], interval2: Interval[T]) = 
    Interval[T](interval1.from min interval2.from, interval1.to max interval2.to) 

    def intersect(interval1: Interval[T], interval2: Interval[T]) = 
    Interval[T](interval1.from max interval2.from, interval1.to min interval2.to) 
} 

implicit def mkIntervalOps[T : Numeric] = new IntervalOps[T] 

que en uso se vería así,

def use[T](i1 : Interval[T], i2 : Interval[T])(implicit ops : IntervalOps[T]) = { 
    import ops._ 
    val i3 = union(i1, i2) 
    val i4 = intersect(i1, i2) 
    (i3, i4) 
} 

Una tercera opciones combina estos dos, utilizando una definición implícita para enriquecer la clase original con los métodos adicionales,

class IntervalOps[T : Numeric](interval1 : Interval[T]) { 
    import Ordering.Implicits._ 

    def union(interval2: Interval[T]) = 
    Interval[T](interval1.from min interval2.from, interval1.to max interval2.to) 

    def intersect(interval2: Interval[T]) = 
    Interval[T](interval1.from max interval2.from, interval1.to min interval2.to) 
} 

implicit def enrichInterval[T : Numeric](interval1 : Interval[T]) = 
    new IntervalOps[T](interval1) 

type Ops[T] = Interval[T] => IntervalOps[T] 

Luego en uso,

def use[T](i1 : Interval[T], i2 : Interval[T])(implicit ops : Ops[T]) = { 
    val i3 = i1 union i2 
    val i4 = i1 intersect i2 
    (i3, i4) 
} 
+0

también, objeto compainon 'Interval' es un buen lugar para poner conversiones implícitas. Luego, en el último ejemplo con el método 'use' solo necesitas pro vide 'Límites de contexto' numéricos 'uso [T: Numérico] (i1: Intervalo [T], i2: Intervalo [T]) ' – 4e6

+0

Gracias Millas. Un montón de molienda para mi molino! –

Cuestiones relacionadas