Características ocultas de Scala


149

¿Cuáles son las características ocultas de Scala que todo desarrollador de Scala debe tener en cuenta?

Una característica oculta por respuesta, por favor.


66
Heh, esta pregunta es tan útil por sus enlaces a otras publicaciones de características ocultas como por la pregunta misma. ¡Salud!
JohnMetta

1
@mettadore solo mira los enlaces relacionados en el lado derecho.
Daniel C. Sobral

2
@ JohnMetta: O use la etiqueta .

Respuestas:


85

Bien, tuve que agregar uno más. Cada Regexobjeto en Scala tiene un extractor (vea la respuesta de oxbox_lakes arriba) que le da acceso a los grupos de coincidencias. Entonces puedes hacer algo como:

// Regex to split a date in the format Y/M/D.
val regex = "(\\d+)/(\\d+)/(\\d+)".r
val regex(year, month, day) = "2010/1/13"

La segunda línea parece confusa si no está acostumbrado a usar patrones coincidentes y extractores. Siempre que defina un valo var, lo que viene después de la palabra clave no es simplemente un identificador sino un patrón. Por eso funciona esto:

val (a, b, c) = (1, 3.14159, "Hello, world")

La expresión de la mano derecha crea un Tuple3[Int, Double, String]que puede coincidir con el patrón (a, b, c).

La mayoría de las veces sus patrones usan extractores que son miembros de objetos singleton. Por ejemplo, si escribes un patrón como

Some(value)

entonces estás llamando implícitamente al extractor Some.unapply.

Pero también puede usar instancias de clase en patrones, y eso es lo que está sucediendo aquí. Val regex es una instancia de Regex, y cuando lo usa en un patrón, está llamando implícitamente regex.unapplySeq( unapplyversus unapplySeqestá más allá del alcance de esta respuesta), que extrae los grupos de coincidencia en a Seq[String], cuyos elementos se asignan para las variables año, mes y día.


1
Gracias por publicar esto! FYI se menciona en el capítulo "Extraer con expresiones regulares" en el libro "Programación en Scala" en la página 503 en la primera edición y en la página 611 en la segunda edición.
Earthling Paul

51

Definiciones de tipo estructural , es decir, un tipo descrito por los métodos que admite. Por ejemplo:

object Closer {
    def using(closeable: { def close(): Unit }, f: => Unit) {
      try { 
        f
      } finally { closeable.close }
    }
}

Observe que el tipo del parámetro closeableno se define más que si tiene un closemétodo


1
Los tipos estructurales ni siquiera se mencionan en "Programación en Scala". Sin embargo, son un poco más lentos que otras técnicas para pasar tipos, ya que usan la reflexión para llamar a los métodos correctos. (Esperemos que encuentren una manera de acelerar eso)
Ken Bloom

1
Y también es posible crear un alias para ellos, lo que funciona como una interfaz asignada externamente (muy lenta): escriba Closeable = {def close (): Unit}
Alexey

45

Polimorfismo de constructor de tipos (también conocido como tipos de tipo superior)

Sin esta característica, puede, por ejemplo, expresar la idea de asignar una función sobre una lista para devolver otra lista, o asignar una función sobre un árbol para devolver otro árbol. Pero no se puede expresar esta idea en general sin clases superiores.

Con tipos más altos, puede capturar la idea de cualquier tipo que esté parametrizado con otro tipo. Se dice que un constructor de tipo que toma un parámetro es de tipo (*->*). Por ejemplo, List. Se dice que un constructor de tipo que devuelve otro constructor de tipo es de tipo (*->*->*). Por ejemplo, Function1. Pero en Scala, tenemos tipos más altos , por lo que podemos tener constructores de tipos que se parametrizan con otros constructores de tipos. Entonces son de tipo como ((*->*)->*).

Por ejemplo:

trait Functor[F[_]] {
  def fmap[A, B](f: A => B, fa: F[A]): F[B]
}

Ahora, si tiene un Functor[List], puede asignar sobre listas. Si tiene un Functor[Tree], puede mapear sobre árboles. Pero lo más importante, si tiene Functor[A] alguna A de tipo(*->*) , puede asignar una función A.


39

Extractores que le permiten reemplazar el if-elseif-elsecódigo de estilo desordenado con patrones. Sé que estos no están exactamente ocultos, pero he estado usando Scala durante unos meses sin comprender realmente el poder de ellos. Para (un largo) ejemplo, puedo reemplazar:

val code: String = ...
val ps: ProductService = ...
var p: Product = null
if (code.endsWith("=")) {
  p = ps.findCash(code.substring(0, 3)) //e.g. USD=, GBP= etc
}
else if (code.endsWith(".FWD")) {
  //e.g. GBP20090625.FWD
  p = ps.findForward(code.substring(0,3), code.substring(3, 9))
}
else {
  p = ps.lookupProductByRic(code)
}

Con esto, que es mucho más claro en mi opinión.

implicit val ps: ProductService = ...
val p = code match {
  case SyntheticCodes.Cash(c) => c
  case SyntheticCodes.Forward(f) => f
  case _ => ps.lookupProductByRic(code)
}

Tengo que hacer un poco de trabajo de fondo en el fondo ...

object SyntheticCodes {
  // Synthetic Code for a CashProduct
  object Cash extends (CashProduct => String) {
    def apply(p: CashProduct) = p.currency.name + "="

    //EXTRACTOR
    def unapply(s: String)(implicit ps: ProductService): Option[CashProduct] = {
      if (s.endsWith("=") 
        Some(ps.findCash(s.substring(0,3))) 
      else None
    }
  }
  //Synthetic Code for a ForwardProduct
  object Forward extends (ForwardProduct => String) {
    def apply(p: ForwardProduct) = p.currency.name + p.date.toString + ".FWD"

    //EXTRACTOR
    def unapply(s: String)(implicit ps: ProductService): Option[ForwardProduct] = {
      if (s.endsWith(".FWD") 
        Some(ps.findForward(s.substring(0,3), s.substring(3, 9)) 
      else None
    }
  }

Pero el trabajo preliminar vale la pena por el hecho de que separa una pieza de lógica empresarial en un lugar sensible. Puedo implementar mis Product.getCodemétodos de la siguiente manera ...

class CashProduct {
  def getCode = SyntheticCodes.Cash(this)
}

class ForwardProduct {
  def getCode = SyntheticCodes.Forward(this)     
}

¿No es esto como un interruptor? Tal vez esto podría ser refactorizado más.
Geo

14
Los patrones son como interruptores turboalimentados: mucho más potentes y claros
oxbow_lakes

1
Bien, pero no me gusta que tengas que usarlo implícitamente porque su alcance va más allá del partido {}. También podría simplemente agregar un método a ProductService que busque un Producto por código. De todos modos, envolvería su fragmento refactorizado en un método para poder usarlo en todas partes.
Martin Konicek

35

Manifiestos que son una forma de obtener la información de tipo en tiempo de ejecución, como si Scala hubiera reificado los tipos.


8
Creo que es preferible explicar la respuesta en la respuesta en lugar de referirse a un enlace. Por cierto, hola agai oxbow! :-)
Daniel C. Sobral

Esta es una característica verdaderamente oculta ... ni siquiera en los documentos API. Muy útil sin embargo.
André Laszlo

35

En scala 2.8 puede tener métodos recursivos de cola utilizando el paquete scala.util.control.TailCalls (de hecho, es trampolín).

Un ejemplo:

def u(n:Int):TailRec[Int] = {
  if (n==0) done(1)
  else tailcall(v(n/2))
}
def v(n:Int):TailRec[Int] = {
  if (n==0) done(5)
  else tailcall(u(n-1))
}
val l=for(n<-0 to 5) yield (n,u(n).result,v(n).result)
println(l)

35

Las clases de casos mezclan automáticamente el rasgo del Producto, proporcionando acceso indexado y sin tipo a los campos sin ninguna reflexión:

case class Person(name: String, age: Int)

val p = Person("Aaron", 28)
val name = p.productElement(0) // name = "Aaron": Any
val age = p.productElement(1) // age = 28: Any
val fields = p.productIterator.toList // fields = List[Any]("Aaron", 28)

Esta característica también proporciona una forma simplificada de alterar la salida del toStringmétodo:

case class Person(name: String, age: Int) {
   override def productPrefix = "person: "
}

// prints "person: (Aaron,28)" instead of "Person(Aaron, 28)"
println(Person("Aaron", 28)) 

32

No está exactamente oculto, pero ciertamente es una característica poco anunciada: scalac -Xprint .

Como ilustración del uso, considere la siguiente fuente:

class A { "xx".r }

Compilando esto con scalac -Xprint: salidas typer :

package <empty> {
  class A extends java.lang.Object with ScalaObject {
    def this(): A = {
      A.super.this();
      ()
    };
    scala.this.Predef.augmentString("xx").r
  }
}

Aviso scala.this.Predef.augmentString("xx").r, que es una aplicación del implicit def augmentStringpresente en Predef.scala.

scalac -Xprint: <phase> imprimirá el árbol de sintaxis después de alguna fase del compilador. Para ver las fases disponibles, use scalac -Xshow-phase .

Esta es una excelente manera de aprender lo que está sucediendo detrás de escena.

Probar con

case class X(a:Int,b:String)

usando la fase typer para sentir realmente lo útil que es.


30

Puede definir sus propias estructuras de control. Realmente son solo funciones y objetos y algo de azúcar sintáctica, pero se ven y se comportan como si fueran reales.

Por ejemplo, el siguiente código define dont {...} unless (cond)y dont {...} until (cond):

def dont(code: => Unit) = new DontCommand(code)

class DontCommand(code: => Unit) {
  def unless(condition: => Boolean) =
    if (condition) code

  def until(condition: => Boolean) = {
    while (!condition) {}
    code
  }
}

Ahora puedes hacer lo siguiente:

/* This will only get executed if the condition is true */
dont {
  println("Yep, 2 really is greater than 1.")
} unless (2 > 1) 

/* Just a helper function */
var number = 0;
def nextNumber() = {
  number += 1
  println(number)
  number
}

/* This will not be printed until the condition is met. */
dont {
  println("Done counting to 5!")
} until (nextNumber() == 5) 


Me gustaría saber si alguien conoce una forma de definir los bloques if-then-else con opcionales más que el tipo de verificación como los estándares.
Philippe

@Philippe: zif[A : Zero](cond: => Boolean)(t: => A): A = if(cond) t else mzero. Requiere Scalaz.
missingfaktor

26

@switch Anotación en Scala 2.8:

Una anotación que se aplicará a una expresión de coincidencia. Si está presente, el compilador verificará que la coincidencia se haya compilado en un conmutador de tabla o de búsqueda y emitirá un error si en su lugar se compila en una serie de expresiones condicionales.

Ejemplo:

scala> val n = 3
n: Int = 3

scala> import annotation.switch
import annotation.switch

scala> val s = (n: @switch) match {
     |   case 3 => "Three"
     |   case _ => "NoThree"
     | }
<console>:6: error: could not emit switch for @switch annotated match
       val s = (n: @switch) match {

26

No sé si esto está realmente oculto, pero me parece bastante agradable.

Los constructores de tipos que toman 2 parámetros de tipo pueden escribirse en notación infija

object Main {                                                                   
  class FooBar[A, B]

  def main(args: Array[String]): Unit = {
    var x: FooBar[Int, BigInt] = null
    var y: Int FooBar BigInt   = null
  }
}

1
¡Agradable! Me imagino que a veces es útil para mejorar la legibilidad. Por ejemplo var foo2barConverter: Foo ConvertTo Bar, el orden de los parámetros de tipo sería evidente.
Esko Luontola

44
A veces hago esto en un código que usa PartialFunction hasta cierto punto: escriba ~> [A, B] = PartialFunction [A, B]
raichoo

24

Scala 2.8 introdujo argumentos predeterminados y con nombre, lo que hizo posible la adición de un nuevo método de "copia" que Scala agrega a las clases de casos. Si define esto:

case class Foo(a: Int, b: Int, c: Int, ... z:Int)

y desea crear un nuevo Foo que sea como un Foo existente, solo que con un valor "n" diferente, entonces simplemente puede decir:

foo.copy(n = 3)

3
ADVERTENCIA: el método de copia no se anulará si hereda una clase de caso de otra. Así que tienes que anularlo manualmente
Alexey

Relacionado: Una forma más limpia de actualizar las estructuras anidadas stackoverflow.com/q/3900307/203968
oluies

55
la clase de caso ya no se permite (Scala 2.8) heredar de una clase de caso. Gracias señor de Scala por despreciar esta herencia impía.
olle kullberg

24

en scala 2.8 puede agregar @specialized a sus clases / métodos genéricos. Esto creará versiones especiales de la clase para tipos primitivos (extendiendo AnyVal) y ahorrará el costo de boxeo / unboxing innecesario: class Foo[@specialized T]...

Puede seleccionar un subconjunto de AnyVals: class Foo[@specialized(Int,Boolean) T]...


1
¿Hay alguna explicación más larga a la que me puedas indicar? Me gustaría aprender más
Paweł Prażak

23

Extendiendo el lenguaje. Siempre quise hacer algo así en Java (no pude). Pero en Scala puedo tener:

  def timed[T](thunk: => T) = {
    val t1 = System.nanoTime
    val ret = thunk
    val time = System.nanoTime - t1
    println("Executed in: " + time/1000000.0 + " millisec")
    ret
  }

y luego escribe:

val numbers = List(12, 42, 3, 11, 6, 3, 77, 44)
val sorted = timed {   // "timed" is a new "keyword"!
  numbers.sortWith(_<_)
}
println(sorted)

y obten

Executed in: 6.410311 millisec
List(3, 3, 6, 11, 12, 42, 44, 77)

23

Puede designar un parámetro de llamada por nombre (EDITADO: ¡esto es diferente a un parámetro diferido!) A una función y no se evaluará hasta que la función lo use (EDITAR: de hecho, se volverá a evaluar cada vez que se usado). Vea este faq para más detalles

class Bar(i:Int) {
    println("constructing bar " + i)
    override def toString():String = {
        "bar with value: " + i
    }
}

// NOTE the => in the method declaration.  It indicates a lazy paramter
def foo(x: => Bar) = {
    println("foo called")
    println("bar: " + x)
}


foo(new Bar(22))

/*
prints the following:
foo called
constructing bar 22
bar with value: 22
*/

Pensé que "x: => Bar" significaba que x era una función que no tomaba parámetros y devolvía una barra. Entonces, "nueva barra (22)" es solo una función anónima, y ​​se evalúa como una función como cualquier otra función.
Alex Black el

1
"x: () => Bar" define una función xa que no toma parámetros y devuelve una barra. x: => La barra define x como llamada por nombre. Eche un vistazo a scala.sygneca.com/faqs/… para más detalles
agilefall

3
Lo que muestra son parámetros de llamada por nombre. Los parámetros diferidos
ArtemGr

Creo que puedes usarlo como un parámetro perezoso si haces algo como lazy val xx: Bar = xen tu método y desde ese momento solo lo usas xx.
Cristian Vrabie

20

Puede usar locallypara introducir un bloque local sin causar problemas de inferencia de punto y coma.

Uso:

scala> case class Dog(name: String) {
     |   def bark() {
     |     println("Bow Vow")
     |   }
     | }
defined class Dog

scala> val d = Dog("Barnie")
d: Dog = Dog(Barnie)

scala> locally {
     |   import d._
     |   bark()
     |   bark()
     | }
Bow Vow
Bow Vow

locally se define en "Predef.scala" como:

@inline def locally[T](x: T): T = x

Al estar en línea, no impone ningún gasto adicional.



17

Inicialización temprana:

trait AbstractT2 {
  println("In AbstractT2:")
  val value: Int
  val inverse = 1.0/value
  println("AbstractT2: value = "+value+", inverse = "+inverse)
}

val c2c = new {
  // Only initializations are allowed in pre-init. blocks.
  // println("In c2c:")
  val value = 10
} with AbstractT2

println("c2c.value = "+c2c.value+", inverse = "+c2c.inverse)

Salida:

In AbstractT2:  
AbstractT2: value = 10, inverse = 0.1  
c2c.value = 10, inverse = 0.1

Instanciamos una clase interna anónima, inicializando el valuecampo en el bloque, antes de la with AbstractT2cláusula. Esto garantiza que valuese inicializa antes de AbstractT2ejecutar el cuerpo de , como se muestra cuando ejecuta el script.


1
El constructo se llama "inicialización temprana".
Randall Schulz

17

Puede componer tipos estructurales con la palabra clave 'con'

object Main {
  type A = {def foo: Unit}
  type B = {def bar: Unit}

  type C = A with B

  class myA {
    def foo: Unit = println("myA.foo")
  }


  class myB {
    def bar: Unit = println("myB.bar")
  }
  class myC extends myB {
    def foo: Unit = println("myC.foo")
  }

  def main(args: Array[String]): Unit = { 
    val a: A = new myA 
    a.foo
    val b: C = new myC 
    b.bar
    b.foo
  }
}

17

sintaxis de marcador de posición para funciones anónimas

De la especificación del lenguaje Scala:

SimpleExpr1 ::= '_'

Una expresión (de categoría sintáctica Expr) puede contener símbolos de subrayado incrustados _en lugares donde los identificadores son legales. Tal expresión representa una función anónima donde las apariciones subsiguientes de guiones bajos denotan parámetros sucesivos.

De Scala Cambios de idioma :

_ + 1                  x => x + 1
_ * _                  (x1, x2) => x1 * x2
(_: Int) * 2           (x: Int) => x * 2
if (_) x else y        z => if (z) x else y
_.map(f)               x => x.map(f)
_.map(_ + 1)           x => x.map(y => y + 1)

Usando esto podrías hacer algo como:

def filesEnding(query: String) =
  filesMatching(_.endsWith(query))

2
Esto debería denominarse 'sintaxis de marcador de posición para funciones anónimas'. Implícito tiene un significado distinto en Scala, y no está relacionado con esto.
retronym

El enlace tiene una relación no obvia con la respuesta. "implícito" no es el término correcto para esto. Como arriba, debe ser "marcador de posición".
Alain O'Dea

2
Realmente no está "oculto", he visto este uso en casi todos los tutoriales en Scala que he leído ... :-) Pero aprecio la definición formal que aún no he visto.
PhiLho

@PhiLho tal vez era menos conocido en 2009. No lo sé.
Eugene Yokota

Perdí la fecha original, ya que solo se muestra la última fecha de edición. Y bueno, no todas las características explicadas en este hilo están "ocultas". Buen hilo y buena respuesta de todos modos.
PhiLho

16

Definiciones implícitas, particularmente conversiones.

Por ejemplo, suponga una función que formateará una cadena de entrada para que se ajuste a un tamaño, reemplazando el centro de la misma con "...":

def sizeBoundedString(s: String, n: Int): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

Puede usar eso con cualquier Cadena y, por supuesto, usar el método toString para convertir cualquier cosa. Pero también podrías escribirlo así:

def sizeBoundedString[T](s: T, n: Int)(implicit toStr: T => String): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

Y luego, podría pasar clases de otros tipos haciendo esto:

implicit def double2String(d: Double) = d.toString

Ahora puedes llamar a esa función pasando un doble:

sizeBoundedString(12345.12345D, 8)

El último argumento es implícito y se pasa automáticamente debido a la declaración implícita. Además, "s" está siendo tratada como una cadena dentro de sizeBoundedString porque hay una conversión implícita de ella a cadena.

Los impedimentos de este tipo están mejor definidos para tipos poco comunes para evitar conversiones inesperadas. También puede pasar explícitamente una conversión, y todavía se usará implícitamente dentro de sizeBoundedString:

sizeBoundedString(1234567890L, 8)((l : Long) => l.toString)

También puede tener múltiples argumentos implícitos, pero luego debe pasarlos todos o no pasar ninguno de ellos. También hay una sintaxis de acceso directo para las conversiones implícitas:

def sizeBoundedString[T <% String](s: T, n: Int): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

Esto se usa exactamente de la misma manera.

Los implicits pueden tener cualquier valor. Se pueden usar, por ejemplo, para ocultar información de la biblioteca. Tome el siguiente ejemplo, por ejemplo:

case class Daemon(name: String) {
  def log(msg: String) = println(name+": "+msg)
}

object DefaultDaemon extends Daemon("Default")

trait Logger {
  private var logd: Option[Daemon] = None
  implicit def daemon: Daemon = logd getOrElse DefaultDaemon

  def logTo(daemon: Daemon) = 
    if (logd == None) logd = Some(daemon) 
    else throw new IllegalArgumentException

  def log(msg: String)(implicit daemon: Daemon) = daemon.log(msg)
}

class X extends Logger {
  logTo(Daemon("X Daemon"))

  def f = {
    log("f called")
    println("Stuff")
  }

  def g = {
    log("g called")(DefaultDaemon)
  }
}

class Y extends Logger {
  def f = {
    log("f called")
    println("Stuff")
  }
}

En este ejemplo, llamar a "f" en un objeto Y enviará el registro al daemon predeterminado y, en una instancia de X, al daemon X de Daemon. Pero llamar a g en una instancia de X enviará el registro al DefaultDaemon dado explícitamente.

Si bien este ejemplo simple puede reescribirse con sobrecarga y estado privado, las implicaciones no requieren un estado privado y pueden ponerse en contexto con las importaciones.


13

Tal vez no esté demasiado oculto, pero creo que esto es útil:

@scala.reflect.BeanProperty
var firstName:String = _

Esto generará automáticamente un getter y setter para el campo que coincida con la convención de bean.

Descripción adicional en developerworks


66
Y puede hacer un acceso directo para él si lo usa mucho, por ejemplo: import scala.reflect. {BeanProperty => BP}
Alexey

13

Argumentos implícitos en los cierres.

Un argumento de función se puede marcar como implícito al igual que con los métodos. Dentro del alcance del cuerpo de la función, el parámetro implícito es visible y elegible para la resolución implícita:

trait Foo { def bar }

trait Base {
  def callBar(implicit foo: Foo) = foo.bar
}

object Test extends Base {
  val f: Foo => Unit = { implicit foo =>
    callBar
  }
  def test = f(new Foo {
    def bar = println("Hello")
  })
}


12

Los tipos de resultados dependen de la resolución implícita. Esto puede darle una forma de envío múltiple:

scala> trait PerformFunc[A,B] { def perform(a : A) : B }
defined trait PerformFunc

scala> implicit val stringToInt = new PerformFunc[String,Int] {
  def perform(a : String)  = 5
}
stringToInt: java.lang.Object with PerformFunc[String,Int] = $anon$1@13ccf137

scala> implicit val intToDouble = new PerformFunc[Int,Double] {
  def perform(a : Int) = 1.0
}
intToDouble: java.lang.Object with PerformFunc[Int,Double] = $anon$1@74e551a4

scala> def foo[A, B](x : A)(implicit z : PerformFunc[A,B]) : B = z.perform(x)
foo: [A,B](x: A)(implicit z: PerformFunc[A,B])B

scala> foo("HAI")
res16: Int = 5

scala> foo(1)
res17: Double = 1.0

Ese podría ser el caso, pero la sesión anterior es engañosa. La definición de fooutiliza un aque debe haber estado presente en el entorno antes de la ejecución de estos comandos. Supongo que quisiste decir z.perform(x).
Daniel C. Sobral

4

Equivalente de Scala al inicializador Java de doble paréntesis.

Scala le permite crear una subclase anónima con el cuerpo de la clase (el constructor) que contiene declaraciones para inicializar la instancia de esa clase.

Este patrón es muy útil cuando se crean interfaces de usuario basadas en componentes (por ejemplo, Swing, Vaadin), ya que permite crear componentes de IU y declarar sus propiedades de manera más concisa.

Consulte http://spot.colorado.edu/~reids/papers/how-scala-experience-improved-our-java-development-reid-2011.pdf para obtener más información.

Aquí hay un ejemplo de cómo crear un botón Vaadin:

val button = new Button("Click me"){
 setWidth("20px")
 setDescription("Click on this")
 setIcon(new ThemeResource("icons/ok.png"))
}

3

Excluir miembros de importdeclaraciones

Suponga que desea utilizar un método Loggerque contenga ay printlnun printerrmétodo, pero solo desea utilizar el que se usa para mensajes de error y mantener el valor antiguo Predef.printlnpara la salida estándar. Podrías hacer esto:

val logger = new Logger(...)
import logger.printerr

pero si loggertambién contiene otros doce métodos que le gustaría importar y usar, resulta inconveniente enumerarlos. En su lugar, podría intentar:

import logger.{println => donotuseprintlnt, _}

pero esto todavía "contamina" la lista de miembros importados. Ingrese el comodín über-poderoso:

import logger.{println => _, _}

y eso hará lo correcto ™.


2

requireMétodo (definido en Predef) que le permite definir restricciones de funciones adicionales que se comprobarían durante el tiempo de ejecución. Imagine que está desarrollando otro cliente de Twitter y necesita limitar la longitud del tweet a 140 símbolos. Además, no puedes publicar un tweet vacío.

def post(tweet: String) = {
  require(tweet.length < 140 && tweet.length > 0) 
  println(tweet)
 }

Ahora llamar a post con un argumento de longitud inapropiada causará una excepción:

scala> post("that's ok")
that's ok

scala> post("")
java.lang.IllegalArgumentException: requirement failed
    at scala.Predef$.require(Predef.scala:145)
    at .post(<console>:8)

scala> post("way to looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong tweet") 
java.lang.IllegalArgumentException: requirement failed
    at scala.Predef$.require(Predef.scala:145)
    at .post(<console>:8)

Puede escribir múltiples requisitos o incluso agregar una descripción a cada uno:

def post(tweet: String) = {
  require(tweet.length > 0, "too short message")
  require(tweet.length < 140, "too long message")
  println(tweet)
}

Ahora las excepciones son detalladas:

scala> post("")
java.lang.IllegalArgumentException: requirement failed: too short message
    at scala.Predef$.require(Predef.scala:157)
    at .post(<console>:8)

Un ejemplo más está aquí .


Prima

Puede realizar una acción cada vez que falle el requisito:

scala> var errorcount = 0
errorcount: Int = 0

def post(tweet: String) = {
  require(tweet.length > 0, {errorcount+=1})
  println(tweet)
  }

scala> errorcount
res14: Int = 0

scala> post("")
java.lang.IllegalArgumentException: requirement failed: ()
    at scala.Predef$.require(Predef.scala:157)
    at .post(<console>:9)
...

scala> errorcount
res16: Int = 1

1
requireNo es una palabra reservada. Es solo un método definido en Predef.
missingfaktor

1

Los rasgos con abstract overridemétodos son una característica en Scala que no se anuncia tan ampliamente como muchos otros. La intención de los métodos con el abstract overridemodificador es hacer algunas operaciones y delegar la llamada a super. Luego, estos rasgos deben mezclarse con implementaciones concretas de sus abstract overridemétodos.

trait A {
  def a(s : String) : String
}

trait TimingA extends A {
  abstract override def a(s : String) = {
    val start = System.currentTimeMillis
    val result = super.a(s)
    val dur = System.currentTimeMillis-start
    println("Executed a in %s ms".format(dur))
    result
  }
}

trait ParameterPrintingA extends A {
  abstract override def a(s : String) = {
    println("Called a with s=%s".format(s))
    super.a(s)
  }
}

trait ImplementingA extends A {
  def a(s: String) = s.reverse
}

scala> val a = new ImplementingA with TimingA with ParameterPrintingA

scala> a.a("a lotta as")
Called a with s=a lotta as
Executed a in 0 ms
res4: String = sa attol a

Si bien mi ejemplo no es mucho más que un AOP pobre para hombres, utilicé estos Rasgos apilables para mi gusto para construir instancias de intérpretes Scala con importaciones predefinidas, enlaces personalizados y patrones de clase. Los Rasgos Apilables hicieron posible crear mi fábrica en la línea de new InterpreterFactory with JsonLibs with LuceneLibsy luego tener importaciones útiles y variables de alcance para los scripts de los usuarios.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.