Hidden features of Scala

2019-01-01 11:47发布

What are the hidden features of Scala that every Scala developer should be aware of?

One hidden feature per answer, please.

28条回答
零度萤火
2楼-- · 2019-01-01 12:05

In scala 2.8 you can have tail-recursive methods by using the package scala.util.control.TailCalls (in fact it's trampolining).

An example:

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)
查看更多
怪性笑人.
3楼-- · 2019-01-01 12:06

Okay, I had to add one more. Every Regex object in Scala has an extractor (see answer from oxbox_lakes above) that gives you access to the match groups. So you can do something like:

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

The second line looks confusing if you're not used to using pattern matching and extractors. Whenever you define a val or var, what comes after the keyword is not simply an identifier but rather a pattern. That's why this works:

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

The right hand expression creates a Tuple3[Int, Double, String] which can match the pattern (a, b, c).

Most of the time your patterns use extractors that are members of singleton objects. For example, if you write a pattern like

Some(value)

then you're implicitly calling the extractor Some.unapply.

But you can also use class instances in patterns, and that is what's happening here. The val regex is an instance of Regex, and when you use it in a pattern, you're implicitly calling regex.unapplySeq (unapply versus unapplySeq is beyond the scope of this answer), which extracts the match groups into a Seq[String], the elements of which are assigned in order to the variables year, month, and day.

查看更多
残风、尘缘若梦
4楼-- · 2019-01-01 12:06

Excluding members from import statements

Suppose you want to use a Logger that contains a println and a printerr method, but you only want to use the one for error messages, and keep the good old Predef.println for standard output. You could do this:

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

but if logger also contains another twelve methods that you would like to import and use, it becomes inconvenient to list them. You could instead try:

import logger.{println => donotuseprintlnt, _}

but this still "pollutes" the list of imported members. Enter the über-powerful wildcard:

import logger.{println => _, _}

and that will do just the right thing™.

查看更多
谁念西风独自凉
5楼-- · 2019-01-01 12:07

Extending the language. I always wanted to do something like this in Java (couldn't). But in Scala I can have:

  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
  }

and then write:

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

and get

Executed in: 6.410311 millisec
List(3, 3, 6, 11, 12, 42, 44, 77)
查看更多
流年柔荑漫光年
6楼-- · 2019-01-01 12:08

Scala 2.8 introduced default and named arguments, which made possible the addition of a new "copy" method that Scala adds to case classes. If you define this:

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

and you want to create a new Foo that's like an existing Foo, only with a different "n" value, then you can just say:

foo.copy(n = 3)
查看更多
余欢
7楼-- · 2019-01-01 12:08

in scala 2.8 you can add @specialized to your generic classes/methods. This will create special versions of the class for primitive types (extending AnyVal) and save the cost of un-necessary boxing/unboxing : class Foo[@specialized T]...

You can select a subset of AnyVals : class Foo[@specialized(Int,Boolean) T]...

查看更多
登录 后发表回答