Scala 的隐藏特性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1025181/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 01:28:17  来源:igfitidea点击:

Hidden features of Scala

scalahidden-features

提问by Krzysiek Goj

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

每个 Scala 开发人员都应该了解 Scala 的哪些隐藏特性?

One hidden feature per answer, please.

请为每个答案添加一个隐藏功能。

回答by Willis Blackburn

Okay, I had to add one more. Every Regexobject 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:

好吧,我不得不再添加一个。RegexScala 中的每个对象都有一个提取器(参见上面 oxbox_lakes 的答案),可让您访问匹配组。因此,您可以执行以下操作:

// 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 valor var, what comes after the keyword is not simply an identifier but rather a pattern. That's why this works:

如果您不习惯使用模式匹配和提取器,第二行看起来会令人困惑。每当您定义valor 时var,关键字后面的内容不仅仅是一个标识符,而是一个模式。这就是为什么这有效:

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

右手边的表达式创建了Tuple3[Int, Double, String]可以匹配模式的 a (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.

那么你隐含地调用了提取器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(unapplyversus unapplySeqis 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.

但是您也可以在模式中使用类实例,这就是这里发生的事情。val regex 是 的一个实例Regex,当您在模式中使用它时,您隐式调用regex.unapplySequnapplyvsunapplySeq超出了本答案的范围),它将匹配组提取到 a 中Seq[String],其中的元素被分配以便变量年、月和日。

回答by oxbow_lakes

Structuraltype definitions - i.e. a type described by what methods it supports. For example:

结构类型定义 - 即由它支持的方法描述的类型。例如:

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

Notice that the typeof the parameter closeableis not defined other than it has a closemethod

请注意,参数的类型closeable没有定义,只是有一个close方法

回答by Apocalisp

Type-Constructor Polymorphism(a.k.a. higher-kinded types)

类型构造函数多态性(又名高级类型)

Without this feature you can, for example, express the idea of mapping a function over a list to return another list, or mapping a function over a tree to return another tree. But you can't express this idea generallywithout higher kinds.

例如,如果没有此功能,您可以表达将函数映射到列表以返回另一个列表,或将函数映射到树以返回另一棵树的想法。但是如果没有更高的种类,你就不能普遍地表达这个想法。

With higher kinds, you can capture the idea of any typethat's parameterised with another type. A type constructor that takes one parameter is said to be of kind (*->*). For example, List. A type constructor that returns another type constructor is said to be of kind (*->*->*). For example, Function1. But in Scala, we have higherkinds, so we can have type constructors that are parameterised with other type constructors. So they're of kinds like ((*->*)->*).

使用更高的种类,您可以捕捉用另一种类型参数化的任何类型的想法。接受一个参数的类型构造函数被称为 kind (*->*)。例如,List。返回另一个类型构造函数的类型构造函数被称为 kind (*->*->*)。例如,Function1。但是在 Scala 中,我们有更高的种类,因此我们可以使用其他类型构造函数参数化的类型构造函数。所以它们就像((*->*)->*).

For example:

例如:

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

Now, if you have a Functor[List], you can map over lists. If you have a Functor[Tree], you can map over trees. But more importantly, if you have Functor[A]for any A of kind (*->*), you can map a function over A.

现在,如果您有Functor[List],则可以映射列表。如果您有Functor[Tree],则可以映射树。但更重要的是,如果您有Functor[A]任何类型的 A(*->*),您可以将函数映射到A

回答by oxbow_lakes

Extractorswhich allow you to replace messy if-elseif-elsestyle code with patterns. I know that these are not exactly hiddenbut I've been using Scala for a few months without really understanding the power of them. For (a long) example I can replace:

提取器,允许您if-elseif-else用模式替换凌乱的样式代码。我知道这些并没有完全隐藏,但我已经使用 Scala 几个月了,但并没有真正理解它们的功能。对于(一个长)示例,我可以替换:

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

With this, which is muchclearer in my opinion

有了这个,我认为这清楚

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

I have to do a bit of legwork in the background...

我必须在后台做一些跑腿工作...

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

But the legwork is worth it for the fact that it separates a piece of business logic into a sensible place. I can implement my Product.getCodemethods as follows..

但是跑腿是值得的,因为它将一段业务逻辑分离到一个合理的地方。我可以实现我的Product.getCode方法如下..

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

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

回答by oxbow_lakes

Manifestswhich are a sort of way at getting the type information at runtime, as if Scala had reified types.

清单是在运行时获取类型信息的一种方式,就好像 Scala 已经具体化了类型。

回答by Aymen

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

在 scala 2.8 中,您可以使用 scala.util.control.TailCalls 包(实际上是蹦床)来使用尾递归方法。

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)

回答by Aaron Novstrup

Case classes automatically mixin the Product trait, providing untyped, indexed access to the fields without any reflection:

Case 类自动混入 Product 特征,提供对字段的无类型索引访问,无需任何反射:

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)

This feature also provides a simplified way to alter the output of the toStringmethod:

此功能还提供了一种更改toString方法输出的简化方法:

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

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

回答by pedrofurla

It's not exactly hidden, but certainly a under advertised feature: scalac -Xprint.

它并没有完全隐藏,但肯定是一个被低估的功能:scalac -Xprint

As a illustration of the use consider the following source:

作为使用说明,请考虑以下来源:

class A { "xx".r }

Compiling this with scalac -Xprint:typeroutputs:

使用scalac -Xprint:typer输出编译它:

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

Notice scala.this.Predef.augmentString("xx").r, which is a the application of the implicit def augmentStringpresent in Predef.scala.

注意scala.this.Predef.augmentString("xx").r,这是implicit def augmentStringPredef.scala中的present的一个应用。

scalac -Xprint:<phase>will print the syntax tree after some compiler phase. To see the available phases use scalac -Xshow-phases.

scalac -Xprint:<phase>将在某个编译器阶段后打印语法树。要查看可用阶段,请使用scalac -Xshow-phases

This is a great way to learn what is going on behind the scenes.

这是了解幕后情况的好方法。

Try with

试试

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

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

using the typerphase to really feel how useful it is.

使用打字机阶段才能真正感受到它的用处。

回答by Aleksander Kmetec

You can define your own control structures. It's really just functions and objects and some syntactic sugar, but they look and behave like the real thing.

您可以定义自己的控制结构。它实际上只是函数和对象以及一些语法糖,但它们的外观和行为就像真实的东西。

For example, the following code defines dont {...} unless (cond)and dont {...} until (cond):

例如,以下代码定义了dont {...} unless (cond)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
  }
}

Now you can do the following:

现在您可以执行以下操作:

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

回答by missingfaktor

@switchannotation in Scala 2.8:

@switchScala 2.8 中的注释:

An annotation to be applied to a match expression. If present, the compiler will verify that the match has been compiled to a tableswitch or lookupswitch, and issue an error if it instead compiles into a series of conditional expressions.

要应用于匹配表达式的注释。如果存在,编译器将验证匹配是否已编译为 tableswitch 或 lookupswitch,如果它编译为一系列条件表达式,则会发出错误。

Example:

例子:

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 {