Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Basis


May 13, 2021 Scala


Table of contents


Basis

About this lesson

The first few weeks will cover basic syntax and concepts, and then we'll expand on them with more practice.

Some examples are given in the form of interpreter interactions, while others are given in the form of source files.

Installing an interpreter can make it easier to explore problem spaces.

Why Scala?

  • The ability to express
    • Functions are first-class citizens
    • Closure
  • Simple
    • Type inference
    • The usage support for function creation
  • Java interoperability
    • The Java library can be reused
    • Java tools can be reused
    • There is no performance penalty

How does Scala work?

  • Compiled into Java bytecode
  • It can be run on any standard JVM
    • Even on some irregular JVMs, such as Dalvik
    • The Scala compiler was written by the author of the Java compiler

Think with Scala

Scala is more than just a better Java. You should learn it with a fresh mind, and you will realize this from these lessons.

Install Scala See: Scala installation and environment configuration

Start the interpreter

Start with the sbt console that come with it.

$ sbt console[...]
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20).
Type in expressions to have them evaluated.Type :help for more information.

scala>

The expression

scala> 1 + 1
res0: Int = 2

res0 is the name of a variable that is automatically created by the interpreter and is used to refer to the evaluation of an expression. It is an Int type with a value of 2.

Everything in Scala is an expression.

Value

You can give the result of an expression a name and assign an invaris (val).

scala> val two = 1 + 1
two: Int = 2

You can't change the value of this constant amount.

Variable

If you need to modify the binding of this name and result, you can choose to var .

scala> var name = "steve"
name: java.lang.String = steve

scala> name = "marius"
name: java.lang.String = marius

Function

You can use def to create functions.

scala> def addOne(m: Int): Int = m + 1
addOne: (m: Int)Int

In Scala, you need to specify a type signature for the function parameters.

scala> val three = addOne(2)
three: Int = 3

If the function does not have arguments, you can not write parentheses.

scala> def three() = 1 + 2
three: ()Int

scala> three()
res2: Int = 3

scala> three
res3: Int = 3

Anonymous function

You can create anonymous functions.

scala> (x: Int) => x + 1
res2: (Int) => Int = <function1>

This function is an Int variable named x plus 1.

scala> res2(1)
res3: Int = 2

You can pass anonymous functions or save them as invariable.

scala> val addOne = (x: Int) => x + 1
addOne: (Int) => Int = <function1>

scala> addOne(1)
res4: Int = 2

If your function has many expressions, you can format the code to make it easy to read.

def timesTwo(i: Int): Int = { 
    println("hello world")  i * 2
}

The same is true for anonymous functions.

scala> { i: Int =>  
    println("hello world")  
    i * 2
}
res0: (Int) => Int = <function1>

This syntax is often used when passing an anonymous function as an argument.

Some apps

You can apply one function using the underscore " S cala uses underscores to represent different things in different contexts, which you can often think of as an unnamed magic wildcard. I t represents an anonymous parameter in the context of ' . You can use it this way:

scala> def adder(m: Int, n: Int) = m + nadder: (m: Int,n: Int)Int

scala> val add2 = adder(2, _:Int)
add2: (Int) => Int = <function1>

scala> add2(3)
res50: Int = 5

You can apply any parameter in the parameter list in part, not just the last one.

Curry function

Sometimes there is a need to allow others to apply some parameters to your function for a while, and then to apply additional parameters.

For example, a multiplication function needs to select a multiplier in one scene, while another scenario needs to select a multiplier.

scala> def multiply(m: Int)(n: Int): Int = m * nmultiply: (m: Int)(n: Int)Int

You can pass in two parameters directly.

scala> multiply(2)(3)
res0: Int = 6

You can fill in the first argument and partially apply the second.

scala> val timesTwo = multiply(2) _
timesTwo: (Int) => Int = <function1>

scala> timesTwo(3)
res1: Int = 6

You can perform Curryization on any multi-argument function. For example, the previous adder function

The first pass is a plus, returning a function, calling the second function pass, and another number, resulting in a result of and

scala> (adder _).curried
res1: (Int) => (Int) => Int = <function1>

scala> res1(2)

res2: (Int) => Int = <function1>

scala> res2(3)

res3: Int = 5

Variable length parameters

This is a special syntax that can pass in as many parameters of the same type to a method. For example, to execute String's capitalize function on more than one string, you can write something like this:

def capitalizeAll(args: String*) = {
    args.map { arg =>
    arg.capitalize
  }
}

scala> capitalizeAll("rarity", "applejack")
res2: Seq[String] = ArrayBuffer(Rarity, Applejack)

Class

scala> class Calculator {
     |   val brand: String = "HP"
         |   def add(m: Int, n: Int): Int = m + n
     | }
defined class Calculator

scala> val calc = new Calculator
calc: Calculator = Calculator@e75a11

scala> calc.add(1, 2)
res1: Int = 3

scala> calc.brand
res2: String = "HP"

The example above shows how to define methods in classes with def and field values with val. A method is a function that can access the state of a class.

The constructor

Constructors are not special methods, they are code other than the method definition of the class. Let's extend the calculator's example, add a constructor argument, and use it to initialize the internal state.

class Calculator(brand: String) {
  /**
   * A constructor.
  */
  val color: String = if (brand == "TI") {
    "blue"
  } else if (brand == "HP") {
    "black"
  } else {
    "white"
  }
  // An instance method.
  def add(m: Int, n: Int): Int = m + n
}

Pay attention to two different styles of comments.

You can use constructors to construct an instance:

scala> val calc = new Calculator("HP")
calc: Calculator = Calculator@1e64cc4d

scala> calc.color
res0: String = black

The expression

The Calculator example above illustrates how Scala is expression-oriented. T he value of the color is bound to if/else expression. Scala is highly expression-oriented: most things are expressions, not instructions.

NARRATOR: Function vs method

Functions and methods are largely interchangeable. B ecause functions and methods are so similar, you probably don't know whether what you're calling is a function or a method. And when you really encounter a difference between methods and functions, you may be confused.

scala> class C {
     |   var acc = 0
     |   def minc = { acc += 1 }
     |   val finc = { () => acc += 1 }
     | }
defined class C

scala> val c = new C
c: C = C@1af1bd6

scala> c.minc // calls c.minc()

scala> c.finc // returns the function as a value:
res2: () => Unit = <function0>

When you can call a "function" without parentheses, but you have to bracket the other, you might think, oh, I thought I knew how Scala works. M aybe they need brackets sometimes? You might think you're using a function, but you're actually using a method.

In practice, you can do great things with Scala, even if you don't understand the difference between methods and functions. I f you're new to Scala, and you're reading about the differences, you may not be able to keep up. B ut that doesn't mean you're in trouble with Scala. It just means that the difference between functions and methods is subtle and can only be understood clearly if you go deep inside the language.

Inherited

class ScientificCalculator(brand: String) extends Calculator(brand) {
  def log(m: Double, base: Double) = math.log(m) / math.log(base)
}

The reference Toa Scala indicates that the type alias is superior to inheritance if the child class is virtually indescessive from the parent class. A Tour of Scala details sub-classification.

Overload method

class EvenMoreScientificCalculator(brand: String) extends ScientificCalculator(brand) {
  def log(m: Int): Double = log(m, math.exp(1))
}

Abstract class

You can define an abstract class that defines methods but does not implement them. I nstead, these methods are defined by sub-classes that extend abstract classes. You cannot create instances of abstract classes.

scala> abstract class Shape {
     |   def getArea():Int    //子类应定义为这个
     | }
defined class Shape

scala> class Circle(r: Int) extends Shape {
     |   def getArea():Int = { r * r * 3 }
     | }
defined class Circle

scala> val s = new Shape
<console>:8: error: class Shape is abstract; cannot be instantiated
       val s = new Shape
               ^
scala> val c = new Circle(2)
c: Circle = Circle@65c0035b

Traits

Traits are collections of fields and behaviors that can be extended or mixed into your class.

trait Car {
  val brand: String
}

trait Shiny {
  val shineRefraction: Int
}

class BMW extends Car {
  val brand = "BMW"
}

With keywords, a class can extend multiple traits:

class BMW extends Car with Shiny {
  val brand = "BMW"
  val shineRefraction = 12
}

Refer to Effective Scala's view of traits.

When should I use traits instead of abstract classes? I f you want to define a type that resembles an interface, you may find it difficult to choose between traits and abstract classes. B oth forms allow you to define some behavior of one type and require the inheritor to define some other behavior. Some rules of experience:

  • Prioritize the use of traits. It is convenient for a class to extend multiple traits, but only one abstract class.
  • If you need constructor parameters, use abstract classes. B ecause abstract classes can define constructors with parameters, and traits don't. For example, you can't say that the parameter i is illegal.

Type

Previously, we defined an argument for a function as Int, indicating that the input is a numeric type. I n fact, functions can also be generic to apply to all types. W hen this happens, you'll see type parameters introduced with square bracket syntax. The following example shows a cache that uses generic keys and values.

trait Cache[K, V] {
  def get(key: K): V
  def put(key: K, value: V)
  def delete(key: K)
}

Methods can also introduce type parameters.

def remove[K](key: K)