Recursos ocultos do Scala


149

Quais são os recursos ocultos do Scala que todo desenvolvedor de Scala deve conhecer?

Um recurso oculto por resposta, por favor.


6
Heh, essa pergunta é tão útil pelos links para outras postagens de recursos ocultos quanto pela própria pergunta. Felicidades!
31510 JohnMetta

1
@mettadore basta olhar para os links relacionados no lado direito.
22710 Daniel C. Sobral

2
@JohnMetta: Ou use a tag .

Respostas:


85

Ok, eu tive que adicionar mais um. Todo Regexobjeto no Scala possui um extrator (veja a resposta de oxbox_lakes acima) que dá acesso aos grupos de correspondência. Então você pode fazer 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"

A segunda linha parece confusa se você não está acostumado a usar correspondência e extratores de padrões. Sempre que você define um valou var, o que vem depois da palavra-chave não é simplesmente um identificador, mas um padrão. É por isso que isso funciona:

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

A expressão da mão direita cria um Tuple3[Int, Double, String]que pode corresponder ao padrão (a, b, c).

Na maioria das vezes, seus padrões usam extratores que são membros de objetos singleton. Por exemplo, se você escrever um padrão como

Some(value)

então você está implicitamente chamando o extrator Some.unapply.

Mas você também pode usar instâncias de classe em padrões, e é isso que está acontecendo aqui. O val regex é uma instância de Regex, e quando você o usa em um padrão, você está implicitamente chamando regex.unapplySeq( unapplyversus unapplySeqestá além do escopo desta resposta), que extrai os grupos de correspondência em a Seq[String], cujos elementos são atribuídos para as variáveis ​​ano, mês e dia.


1
Thx por postar isso! Para sua informação, é mencionado no capítulo "Extraindo com expressões regulares" no livro "Programação em Scala" na página 503 na primeira edição e na página 611 na segunda edição.
earthling paul

51

Definições de tipo estrutural - ou seja, um tipo descrito por quais métodos ele suporta. Por exemplo:

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

Observe que o tipo do parâmetro closeablenão está definido, exceto por ter um closemétodo


1
Os tipos estruturais nem são mencionados em "Programação no Scala". Eles são um pouco mais lentos do que outras técnicas para passar tipos, pois usam a reflexão para chamar os métodos certos. (Esperemos que eles vão vir para cima com uma forma de acelerar isso.)
Ken Bloom

1
E também é possível fazer alias para eles, o que funciona como interface atribuído externamente (um muito lento): Tipo de closeable = {def close (): Unidade}
Alexey

45

Polimorfismo de construtor de tipo (também conhecido como tipos com classificação mais alta)

Sem esse recurso, você pode, por exemplo, expressar a idéia de mapear uma função sobre uma lista para retornar outra lista ou mapear uma função sobre uma árvore para retornar outra árvore. Mas você não pode expressar essa ideia geralmente sem tipos mais elevados.

Com tipos mais altos, você pode capturar a ideia de qualquer tipo parametrizado com outro tipo. Diz-se que um construtor de tipos que usa um parâmetro é do tipo (*->*). Por exemplo List,. Diz-se que um construtor de tipos que retorna outro construtor de tipos é do tipo (*->*->*). Por exemplo Function1,. Porém, no Scala, temos tipos mais altos , portanto, podemos ter construtores de tipo que são parametrizados com outros construtores de tipo. Então eles são do tipo ((*->*)->*).

Por exemplo:

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

Agora, se você tiver um Functor[List], poderá mapear as listas. Se você tiver um Functor[Tree], poderá mapear sobre árvores. Mas o mais importante é que, se você tiver Functor[A] algum tipo A(*->*) , poderá mapear uma função A.


39

Extratores que permitem substituir o if-elseif-elsecódigo de estilo confuso por padrões. Sei que eles não estão exatamente ocultos, mas estou usando o Scala há alguns meses sem realmente entender o poder deles. Para (um longo) exemplo, eu posso substituir:

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)
}

Com isso, que é muito mais claro na minha opinião

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

Eu tenho que fazer um pouco de trabalho braçal em segundo plano ...

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

Mas o trabalho braçal vale a pena pelo fato de separar uma parte da lógica de negócios em um local sensato. Eu posso implementar meus Product.getCodemétodos da seguinte maneira ..

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

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

isso não é como um interruptor? talvez isso possa ser refatorado mais.
Geo

14
Padrões são como interruptores turbo: muito mais poderosa e clara
oxbow_lakes

1
Legal, mas não gosto que você precise usar implícito porque seu escopo ultrapassa a correspondência {}. Você também pode adicionar um método ao ProductService que procura um produto por código. De qualquer maneira, você agruparia seu snippet refatorado em um método para poder usá-lo em qualquer lugar.
Martin Konicek

35

Manifesta que são uma forma de obter as informações de tipo em tempo de execução, como se Scala tivesse reificado tipos.


8
Eu acho que é preferível explicar a resposta na resposta, em vez de se referir a um link. A propósito, oi agai oxbow! :-)
Daniel C. Sobral

Esse é um recurso realmente oculto ... nem mesmo nos documentos da API. Muito útil.
André Laszlo

35

No scala 2.8, você pode ter métodos recursivos de cauda usando o pacote scala.util.control.TailCalls (na verdade, é um trampolim).

Um exemplo:

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

As classes de caso combinam automaticamente a característica do Produto, fornecendo acesso indexado e sem tipo aos campos sem nenhum reflexo:

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)

Esse recurso também fornece uma maneira simplificada de alterar a saída do 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

Não é exatamente oculto, mas certamente um recurso sub-anunciado: scalac -Xprint .

Como ilustração do uso, considere a seguinte fonte:

class A { "xx".r }

Compilando isso com scalac -Xprint: typer outputs:

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

Observe scala.this.Predef.augmentString("xx").r, que é a aplicação do implicit def augmentStringpresente no Predef.scala.

scalac -Xprint: <phase> imprimirá a árvore de sintaxe após alguma fase do compilador. Para ver as fases disponíveis, use scalac -Xshow-phase .

Essa é uma ótima maneira de aprender o que está acontecendo nos bastidores.

Tente com

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

usando a fase typer para realmente sentir como é útil.


30

Você pode definir suas próprias estruturas de controle. É realmente apenas funções e objetos e um pouco de açúcar sintático, mas eles parecem e se comportam como a coisa real.

Por exemplo, o código a seguir define dont {...} unless (cond)e 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
  }
}

Agora você pode fazer o seguinte:

/* 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) 


Eu ficaria curioso para saber se alguém sabe como definir blocos if-then-else com outro opcional que verifica o tipo como os padrão.
Philippe

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

26

@switch anotação no Scala 2.8:

Uma anotação a ser aplicada a uma expressão de correspondência. Se presente, o compilador verificará se a correspondência foi compilada em um comutador de tabela ou de pesquisa e emitirá um erro se, em vez disso, compilar em uma série de expressões condicionais.

Exemplo:

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

Não sei se isso está realmente oculto, mas acho muito bom.

Os construtores de tipos que usam dois parâmetros de tipo podem ser escritos em notação infix

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
Agradável! Eu posso imaginar que às vezes seja útil para melhorar a legibilidade. Por exemplo var foo2barConverter: Foo ConvertTo Bar, tornaria evidente a ordem dos parâmetros de tipo.
Esko Luontola

4
Às vezes eu fazer isso no código que usa função parcial, até certo ponto: Tipo de ~> [A, B] = função parcial [A, B]
raichoo

24

O Scala 2.8 introduziu argumentos padrão e nomeados, o que possibilitou a adição de um novo método de "cópia" que o Scala adiciona às classes de caso. Se você definir isso:

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

e você deseja criar um novo Foo que seja como um Foo existente, apenas com um valor "n" diferente, basta dizer:

foo.copy(n = 3)

3
AVISO: o método copy não será substituído se você herdar uma classe de caso de outra. Então você tem que substituí-lo manualmente
Alexey

Relacionado:
Maneira mais

5
a classe de caso não pode mais (Scala 2.8) herdar de uma classe de caso. Obrigado senhor de Scala por depreciar essa herança profana.
Olle kullberg 11/10/10

24

no scala 2.8, você pode adicionar @specialized às suas classes / métodos genéricos. Isso criará versões especiais da classe para tipos primitivos (estendendo o AnyVal) e economize o custo do boxe / unboxing desnecessário: class Foo[@specialized T]...

Você pode selecionar um subconjunto de AnyVals: class Foo[@specialized(Int,Boolean) T]...


1
Existe uma explicação mais longa para a qual você poderia me indicar? Eu gostaria de aprender mais.
Paweł Prażak

23

Estendendo o idioma. Eu sempre quis fazer algo assim em Java (não podia). Mas em Scala eu posso ter:

  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
  }

e escreva:

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

e pegue

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

23

Você pode designar um parâmetro de chamada por nome (EDITADO: é diferente de um parâmetro lento!) Para uma função e não será avaliado até que seja usado pela função (EDIT: na verdade, será reavaliado toda vez que for usava). Veja este FAQ para detalhes

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
*/

Eu pensei que "x: => Bar" significava que x era uma função que não aceitava parâmetros e retornava um Bar. Portanto, "new bar (22)" é apenas uma função anônima e é avaliada como uma função como qualquer outra função.
217 Alex Alex

1
"x: () => Bar" define xa função que não aceita parâmetros e retorna uma barra. x: => Barra define x como chamada pelo nome. Dê uma olhada em scala.sygneca.com/faqs/... para mais detalhes
agilefall

3
O que você mostra são parâmetros de chamada por nome. Parâmetros preguiçosos ainda não foram implementados: lampsvn.epfl.ch/trac/scala/ticket/240
ArtemGr

Eu acho que você pode usá-lo como um parâmetro preguiçoso se você fizer algo como lazy val xx: Bar = xno seu método e a partir desse momento você só usar xx.
Cristian Vrabie

20

Você pode usar locallypara introduzir um bloco local sem causar problemas de inferência de ponto e vírgula.

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 é definido em "Predef.scala" como:

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

Estando em linha, ele não impõe nenhuma sobrecarga adicional.



17

Inicialização antecipada:

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)

Resultado:

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

Instanciamos uma classe interna anônima, inicializando o valuecampo no bloco, antes da with AbstractT2cláusula. Isso garante que valueé inicializado antes da AbstractT2execução do corpo , conforme mostrado quando você executa o script.


1
A construção é chamada "inicialização antecipada".
Randall Schulz

17

Você pode compor tipos estruturais com a palavra-chave 'with'

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

sintaxe de espaço reservado para funções anônimas

Da especificação da linguagem Scala:

SimpleExpr1 ::= '_'

Uma expressão (da categoria sintática Expr) pode conter símbolos de sublinhado incorporados _em locais onde os identificadores são legais. Essa expressão representa uma função anônima em que ocorrências subsequentes de sublinhados indicam parâmetros sucessivos.

Das alterações de idioma do Scala :

_ + 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 isso, você pode fazer algo como:

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

2
Isso deve ser chamado de 'sintaxe de espaço reservado para funções anônimas'. Implícito tem um significado distinto em Scala, e não está relacionado a isso.
retronym

O link tem um relacionamento não óbvio com a resposta. "implícito" não é o termo correto para isso. Como acima, ele deve ser "espaço reservado".
Alain O'Dea

2
Não é realmente "oculto", eu já vi esse uso em quase todos os tutoriais sobre o Scala que li ... :-) Mas aprecio a definição formal que ainda não vi.
PhiLho

@ PhiLho talvez fosse menos conhecido em 2009. eu não sei.
19611 Eugene Yokota

Perdi a data original, pois apenas a data da última edição é exibida. E bem, nem todos os recursos explicados neste tópico estão "ocultos". Fio legal e boa resposta de qualquer maneira.
PhiLho

16

Definições implícitas, principalmente conversões.

Por exemplo, assuma uma função que formate uma sequência de entrada para caber em um tamanho, substituindo o meio por "...":

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
}

Você pode usar isso com qualquer String e, é claro, usar o método toString para converter qualquer coisa. Mas você também pode escrever assim:

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
}

E então, você pode passar classes de outros tipos fazendo o seguinte:

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

Agora você pode chamar essa função passando um duplo:

sizeBoundedString(12345.12345D, 8)

O último argumento está implícito e está sendo passado automaticamente devido à declaração implícita. Além disso, "s" está sendo tratado como uma String dentro de sizeBoundedString porque há uma conversão implícita para String.

Implícitos desse tipo são melhor definidos para tipos incomuns, a fim de evitar conversões inesperadas. Você também pode passar explicitamente uma conversão, e ela ainda será usada implicitamente dentro de sizeBoundedString:

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

Você também pode ter vários argumentos implícitos, mas deve passar por todos eles ou não por nenhum deles. Há também uma sintaxe de atalho para conversões 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
}

Isso é usado exatamente da mesma maneira.

Implícitos podem ter qualquer valor. Eles podem ser usados, por exemplo, para ocultar informações da biblioteca. Veja o exemplo a seguir, por exemplo:

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")
  }
}

Neste exemplo, chamar "f" em um objeto Y enviará o log para o daemon padrão e em uma instância de X para o daemon X do Daemon. Mas chamar g em uma instância do X enviará o log para o DefaultDaemon explicitamente fornecido.

Embora este exemplo simples possa ser reescrito com sobrecarga e estado privado, os implícitos não requerem estado privado e podem ser contextualizados com importações.


13

Talvez não seja muito oculto, mas acho que isso é útil:

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

Isso gerará automaticamente um getter e um setter para o campo que corresponde à convenção do bean.

Descrição adicional em developerworks


6
E você pode criar um atalho para isso se você o usar muito, por exemplo: import scala.reflect. {BeanProperty => BP}
Alexey

13

Argumentos implícitos nos fechamentos.

Um argumento de função pode ser marcado como implícito, assim como nos métodos. Dentro do escopo do corpo da função, o parâmetro implícito é visível e elegível para resolução 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

Os tipos de resultados dependem da resolução implícita. Isso pode fornecer uma forma de envio múltiplo:

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

Pode ser o caso, mas a sessão acima é enganosa. A definição de foousos aque devem estar presentes no ambiente antes da execução desses comandos. Eu suponho que você quis dizer z.perform(x).
Daniel C. Sobral

4

O equivalente da Scala ao inicializador de chave dupla Java.

Scala permite criar uma subclasse anônima com o corpo da classe (o construtor) contendo instruções para inicializar a instância dessa classe.

Esse padrão é muito útil ao criar interfaces de usuário baseadas em componentes (por exemplo, Swing, Vaadin), pois permite criar componentes da interface do usuário e declarar suas propriedades de forma mais concisa.

Consulte http://spot.colorado.edu/~reids/papers/how-scala-experience-improved-our-java-development-reid-2011.pdf para obter mais informações.

Aqui está um exemplo de criação de um botão Vaadin:

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

3

Excluindo membros de importdeclarações

Suponha que você queira usar um Loggerque contenha um printlne um printerrmétodo, mas deseje usar apenas aquele para mensagens de erro e mantenha o bom e velho estilo Predef.printlnpara a saída padrão. Você poderia fazer isso:

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

mas se loggertambém contiver outros doze métodos que você gostaria de importar e usar, será inconveniente listá-los. Você poderia tentar:

import logger.{println => donotuseprintlnt, _}

mas isso ainda "polui" a lista de membros importados. Digite o curinga super poderoso:

import logger.{println => _, _}

e isso fará a coisa certa ™.


2

require(definido em Predef) que permite definir restrições adicionais de função que seriam verificadas durante o tempo de execução. Imagine que você está desenvolvendo outro cliente do twitter e precisa limitar o tamanho do tweet até 140 símbolos. Além disso, você não pode postar um tweet vazio.

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

Agora, chamar post com argumento de comprimento inadequado causará uma exceção:

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)

Você pode escrever vários requisitos ou até mesmo adicionar uma descrição a cada um:

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

Agora as exceções são detalhadas:

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

Mais um exemplo está aqui .


Bônus

Você pode executar uma ação sempre que o requisito falhar:

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
requirenão é uma palavra reservada. É apenas um método definido em Predef.
missingfaktor

1

Traços com abstract overridemétodos são um recurso do Scala que não é tão amplamente divulgado como muitos outros. A intenção dos métodos com o abstract overridemodificador é executar algumas operações e delegar a chamada para super. Então essas características precisam ser combinadas com implementações concretas de seus 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

Embora meu exemplo não seja muito mais do que um pobre AOP, usei essas características empilháveis para criar instâncias de interpretador Scala com importações predefinidas, ligações personalizadas e caminhos de classe. As características empilháveis tornaram possível criar minha fábrica de acordo com as linhas de new InterpreterFactory with JsonLibs with LuceneLibse, em seguida, ter importações úteis e variáveis ​​de escopo para os scripts dos usuários.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.