Kika's
Blog
图片简介 | CC BY 4.0 | 换一张

Scala3 Memo

2023-12-24

Basic

// Types
// Any is the supertype of all types, Nothing is the subtype of all types

// String
val x = -1
println(s"x.abs = ${x.abs}")   // "x.abs = 1"
val name = "James"
val age = 30
println(s"$name is $age years old")   // "James is 30 years old"

// Functions
val addOne = (x: Int) => x + 1
println(addOne(1)) // 2

// Methods
// note: The last expression in the body is the method’s return value. 
def add(x: Int, y: Int): Int = x + y
println(add(1, 2)) // 3

// Classes
// note: void in C++, Unit in Scala
// note: In Scala 3 you can make an instance of a class without new keyword
// default parameters
class Point(var x: Int = 0, var y: Int = 0)
// 
class Point:
  private var _x = 0

  def x: Int = _x
  def x_=(newValue: Int): Unit = // def x_= is for validating and setting the value of _x
      _x = newValue
end Point
val point1 = Point()
point1.x = 99

// Traits
// cannot have constructor parameters
trait Iterator[A]:
  def hasNext: Boolean
  def next(): A
class IntIterator(to: Int) extends Iterator[Int]:
  private var current = 0
  override def hasNext: ???
  override def next(): ???
end IntIterator
// where a given trait is required, a subtype of the trait can be used instead:
trait Pet:
  val name: String
class Cat(val name: String) extends Pet
val cat = Cat("Sally")

// Tuple
// a tuple can hold a sequence of different types.
// Scala3:
println(ingredient(0)) // Sugar
println(ingredient(1)) // 25
// Scala2:
println(ingredient._1) // Sugar
println(ingredient._2) // 25
//
val (name, quantity) = ingredient
println(name)     // Sugar
println(quantity) // 25
//
val numPairs = List((2, 5), (3, -7), (20, 56))
for (a, b) <- numPairs do
  println(a * b)

// Seq
// an ordered collection of elements (also called a sequence)
val numbers = Seq (1, 15, -2, 0)
val second = numbers (1)

// Array // fill n element Array.fill(n)(element) // fill n block return values Array.fill(n){ block }

类混合

TODO

函数是一等公民

functions are first-class values in Scala

Use the phrase “higher order function” for both methods and functions that take functions as parameters or that return a function:

val salaries = Seq(20_000, 70_000, 40_000)
val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000)
// or just
val newSalaries = salaries.map(_ * 2)

Nest method definitions:

def factorial(x: Int): Int =
  def fact(x: Int, accumulator: Int): Int =
    if x <= 1 then accumulator
    else fact(x - 1, x * accumulator)
  fact(x, 1)

模式匹配

sealed关键字可以让编译器检查case是否考虑完全.

// Matching on case classes
sealed trait Notification // This provides extra safety because the compiler checks that the cases of a match expression are exhaustive when the base type is sealed
def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String =
  notification match
    case Email(sender, _, _) if importantPeopleInfo.contains(sender) =>
      "You got an email from special someone!"
    case SMS(number, _) if importantPeopleInfo.contains(number) =>
      "You got an SMS from special someone!"
    case other =>
      showNotification(other) // nothing special, delegate to our original showNotification function

val importantPeopleInfo = Seq("867-5309", "jenny@gmail.com")
val someSms = SMS("123-4567", "Are you there?")
val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")
val importantEmail = Email("jenny@gmail.com", "Drinks tonight?", "I'm free after 5!")
val importantSms = SMS("867-5309", "I'm here! Where are you?")

println(showImportantNotification(someSms, importantPeopleInfo)) // prints You got an SMS from 123-4567! Message: Are you there?
println(showImportantNotification(someVoiceRecording, importantPeopleInfo)) // prints You received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123
println(showImportantNotification(importantEmail, importantPeopleInfo)) // prints You got an email from special someone!
println(showImportantNotification(importantSms, importantPeopleInfo)) // prints You got an SMS from special someone!

伴随对象

Like static in C++ Class?

import scala.math.{Pi, pow}

case class Circle(radius: Double):
  import Circle.*
  def area: Double = calculateArea(radius)
object Circle:
  private def calculateArea(radius: Double): Double = Pi * pow(radius, 2.0)

val circle1 = Circle(5.0)
circle1.area

Factory method:

class Email(val username: String, val domainName: String)

object Email:
  def fromString(emailString: String): Option[Email] = 
    emailString.split('@') match
      case Array(a, b) => Some(Email(a, b))
      case _ => None

val scalaCenterEmail = Email.fromString("scala.center@epfl.ch")
scalaCenterEmail match
  case Some(email) => println(
    s"""Registered an email
       |Username: ${email.username}
       |Domain name: ${email.domainName}
     """.stripMargin)
  case None => println("Error: could not parse email")

apply和unapply

import scala.util.Random

object CustomerID {
  def apply(name: String) = s"$name--${Random.nextLong()}"
  def unapply(customerID: String): Option[String] = {
    val stringArray: Array[String] = customerID.split("--")
    if (stringArray.tail.nonEmpty) Some(stringArray.head) else None
  }
}

val customer1ID = CustomerID("Sukyoung")  // Sukyoung--23098234908
customer1ID match {
  case CustomerID(name) => println(name)  // prints Sukyoung
  case _ => println("Could not extract a CustomerID")
}
val customer2ID = CustomerID("Nico")
val CustomerID(name) = customer2ID
println(name)  // prints Nico

val customer1ID = CustomerID("Sukyoung")时,调用apply().当CustomerID(name) => println(name)时,调用unapply()

型变 Variance

泛型父子关系控制?

class Foo[+A] // A covariant class
class Bar[-A] // A contravariant class
class Baz[A]  // An invariant class

协变[+T]

given some class Cov[+T], then if A is a subtype of B, Cov[A] is a subtype of Cov[B]

逆变[-T]

given some class Contra[-T], then if A is a subtype of B, Contra[B] is a subtype of Contra[A]

例如一个序列化器:

abstract class Animal {
  def name: String
}
case class Cat(name: String) extends Animal

abstract class Serializer[-A]:
  def serialize(a: A): String

val animalSerializer: Serializer[Animal] = new Serializer[Animal]():
  def serialize(animal: Animal): String = s"""{ "name": "${animal.name}" }"""

val catSerializer: Serializer[Cat] = animalSerializer
catSerializer.serialize(Cat("Felix"))

不变[T]

given some class Inv[T], then although A is a subtype of B, Inv[A] is not a subtype of Inv[B]

类型边界约束

类型上界

class PetContainer[P <: Pet](p: P) {
  def pet: P = p
}

T <: A这样声明的类型上界表示类型变量T应该是类型A的子类

类型下界

>: A表示类型参数B或抽象类型B是类型A的超类型

其他糖

  • 在 Scala 中,??? 是一个便捷的方法,用于表示一个尚未实现的代码部分。
  • for Comprehensions
def foo(n: Int, v: Int) =
   for i <- 0 until n
       j <- 0 until n if i + j == v
   yield (i, j)

foo(10, 10).foreach {
  (i, j) => println(s"($i, $j) ")  // prints (1, 9) (2, 8) (3, 7) (4, 6) (5, 5) (6, 4) (7, 3) (8, 2) (9, 1)
}

和C/C++的不同

  • Scala中的类构造参数自动成为成员变量,而C++构造函数中参数只是局部变量,在Scala中想要获得类似的效果,应该让类构造参数变成私有的
  • Scala中函数是一等公民