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:09

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 generally without higher kinds.

With higher kinds, you can capture the idea of any type that'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 higher kinds, so we can have type constructors that are parameterised with other type constructors. So they're of kinds like ((*->*)->*).

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.

查看更多
余欢
3楼-- · 2019-01-01 12:10

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

查看更多
情到深处是孤独
4楼-- · 2019-01-01 12:10

Scala's equivalent of Java double brace initializer.

Scala allows you to create an anonymous subclass with the body of the class (the constructor) containing statements to initialize the instance of that class.

This pattern is very useful when building component-based user interfaces (for example Swing , Vaadin) as it allows to create UI components and declare their properties more concisely.

See http://spot.colorado.edu/~reids/papers/how-scala-experience-improved-our-java-development-reid-2011.pdf for more information.

Here is an example of creating a Vaadin button:

val button = new Button("Click me"){
 setWidth("20px")
 setDescription("Click on this")
 setIcon(new ThemeResource("icons/ok.png"))
}
查看更多
后来的你喜欢了谁
5楼-- · 2019-01-01 12:12

Maybe not too hidden, but I think this is useful:

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

This will automatically generate a getter and setter for the field that matches bean convention.

Further description at developerworks

查看更多
有味是清欢
6楼-- · 2019-01-01 12:14

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

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 much clearer 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.getCode methods as follows..

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

class ForwardProduct {
  def getCode = SyntheticCodes.Forward(this)     
}
查看更多
千与千寻千般痛.
7楼-- · 2019-01-01 12:15

You can designate a call-by-name parameter (EDITED: this is different then a lazy parameter!) to a function and it will not be evaluated until used by the function (EDIT: in fact, it will be reevaluated every time it is used). See this faq for details

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
*/
查看更多
登录 后发表回答