高效的 scala henrik engstrÖm typesafe 软件工程师 @h3nk3. ✦t✦t ypesafe...

Post on 18-Jan-2016

334 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

高效的 ScalaHENRIK ENGSTRÖM

TYPESAFE 软件工程师@h3nk3

✦ Typesafe 控制台技术负责人✦ Akka 团队荣誉会员

✦ 从 2010 年开始进行 Scala 编程

自我介绍✦ 从 1998 年开始做咨询 — 主要针对

Java

✦ 从 2011 年开始为 Typesafe 公司工作

✦ 阿森纳球迷 + 武术家

关于 Typesafe

✦ Typesafe 平台‣ Play 、 Akka 、 Scala 、 Scala

IDE 、 Slick 、 SBT 等✦ 订阅✦ 培训和咨询

议题✦ 基础知识✦ Scala 中的对象定位✦ 隐式✦ 类型✦ 集合✦ 模式匹配✦ 函数式编程

基础知识

// REPL = Read Eval Print Loop$ scala_home/bin/scalaWelcome to Scala version 2.10.0scala> println("Hello, world!")Hello, world!scala>

使用 REPL

// Put JAR files in scala_home/lib to get access$ scala_home/bin/scalaWelcome to Scala version 2.10.0scala> import com.x.y.z.MyClassscala> val instance = new MyClassscala> instance.myMethod

REPL 和 JAR

REPL 2013✦ IDE 工作表

- Scala IDE : 棒极了- IntelliJ : 很好

// JAVAString result = null;if (z < 9) result = "<9" else result = ">=9";System.out.println("Result: " + result);// SCALAprintln("Result: " + if (z < 9) "<9" else ">=9"))

表达式与语句

// What type is variable quiz?var x = 1val quiz = while (x < 10) { println("X is: " + x) x += 1}

语句突击测试 (Pop Quiz)

✦ Scala 中的所有表达式返回一个类型

// Don’t tell the computer how to fishimport scala.collection.mutable.{HashSet => MHS}def findPeopleInCity(c: String, ppl: Seq[People]): Set[People] = { val found = new MHS[People]() for (p <- ppl) for (a <- p.address) if (a.city == c) found.put(p) found}

请注意无钓鱼说明!

def findPeopleInCity(c: String, ppl: Seq[People]): Set[People] = { for { p <- ppl.toSet[People] a <- p.address if a.city == c } yield p}// SQL LIKE SYNTAX; FROM, WHERE, AND, SELECT

而只是点鱼

// 1) Mutable code leads to cloning // 2) Cloning leads to performance degradation// => Mutable code leads to worse performanceclass Footballer { private var cars = Array[Car]() def setCars(c: Array[Car]): Unit = cars = c.clone def getCars: Array[Car] = cars.clone}

保持不可变

// Safer code - use immutable collectionclass Footballer { private var cars = Vector.empty[Car] def setCars(c: Vector[Car]) = cars = c def getCars: Vector[Car] = cars}

保持不可变

// Case classes make the class immutablescala> case class Car(brand: String)scala> case class Footballer(name: String, team: String, cars: Vector[Car] = Vector.empty)scala> var jw = new Footballer("Jack Wilshire", "Arsenal")Footballer(Jack Wilshire, Arsenal, Vector())scala> jw = jw.copy(cars = Vector(Car("Porsche")))Footballer(Jack Wilshire, Arsenal, Vector(Car(Porsche)))

Case 类 ftw

不可变的优势✦ 简单均等✦ 简单散列代码✦ 无需锁定✦ 无防御性复制✦ Scala Case 类

‣ 自动均等 + 散列代码 (Murmur)

‣ 还有很多其他好处(例如复制)

// Sometimes local mutability makes senseimport scala.collection.mutable.ArrayBufferclass MyClass { def theData: Seq[Int] = { val buffer = new ArrayBuffer[Int]() populateData(buffer) buffer.toSeq }}

局部可变性

// A classic game of nulldef auth(usr: String, pwd: String): Privileges = if (usr == null || usr.isEmpty || pwd == null || pwd.isEmpty || !validCredentials(usr, pwd)) withPrivileges(Anonymous) else privilegesFor(usr)

使用选项

def auth(usr: Option[String], pwd: Option[String]): Privileges = { val privileges: Option[Privileges] = { u <- usr p <- pwd if (!u.isEmpty && !p.isEmpty) if canAuthenticate(u, p) } yield privilegesFor(u) privileges getOrElse withPrivileges(Anonymous)}

使用选项

对象定位

trait SquareShape { val width: Int val height: Int val area: Int = width * height}class Rect(w: Int, h: Int) extends SquaredShape { override val width = w override val height = h}scala> val r1 = new Rectangle(1, 314)scala> r1.heightres0: Int = 314scala> r1.areares1: Int = 0

抽象成员的 val

Scala 初始化顺序

✦ 摘自 Scala 规范(第 5.1 节)- http://www.scala-lang.org/docu/files/

ScalaReference.pdf

‣ 首先评估超类构建函数‣ 然后是模板线性化中的所有基类……

‣ 最后评估语句序列状态

trait SquareShape { def width: Int def height: Int def area: Int = width * height}class Rect(w: Int, h: Int) extends SquaredShape { override val width = w override val height = h}// or even bettercase class Rect(width: Int, height: Int) extends SquaredShape

def is much better

// Non-trivial return types should always be// annotated!def convert(x: Int) = x match { case 1 => 1.toChar case 2 => true case z => z.toByte}def convert(x: Int): AnyVal = x match {

为您的 API 加批注

组合与继承

✦ 与继承相比,更偏好组合- 易于修改(例如 DI )

✦ 组合可在 Scala 中使用继承- 引向著名的“蛋糕”模式

trait UserRepoComponent { def userLocator: UserLocator def userUpdater: UserUpdater trait UserLocator { def findAll: Vector[User] } trait UserUpdater { def save(user: User) }}

让我们来烘培一块“蛋糕”

trait JPAUserRepoComponent extends UserRepoComponent { def em: EntityManager def userLocator = new JPAUserLocator(em) def userUpdater = new JPAUserUpdater(em) class JPAUserLocator(em: EntityManager) extends UserLocator { def findAll: Vector[User] = em.createQuery("from User", classOf[User]).getResultList.toVector } class JPAUserUpdater(em: EntityManager) extends UserUpdater { def save(user: User) = em.persist(user) }}

正在烘培

trait UserServiceComponent { def userService: UserService trait UserService { def findAll: Vector[User] def save(user: User): Unit def checkStatusOf(user: User): String }}

服务层

trait DefaultUserServiceComponent extends UserServiceComponent { this: UserRepositoryComponent => def userService = new DefaultUserService class DefaultUserService extends UserService { def findAll = userLocator.findAll def save(user: User) = userUpdater.save(user) def checkStatus(user: User) = s"User $user seems okay to me" }}

服务层实现

object MyApplication extends Application { val compService = new DefaultUserServiceComponent with JPAUserRepositoryComponent { def em = Persistence.createEntityManagerFactory( "cakepattern").createEntityManager() } val service = compService.userService // use the service}

投入使用

class MyTest extends WordSpec with MustMatchers with Mockito { trait MockedEntityManager { def em = mock[EntityManager] } "service" must { "return all users" in { val compService = new DefaultUserServiceComponent with JPAUserRepositoryComponent with MockedEntityManager // perform tests }}

进行测试

隐式

有何作用?

✦ 在特定上下文中移除样板代码‣ 编译时安全‣ 但是必须明确

trait AutoRepository { def find(regId: String)(implicit dbId: DBId): Option[Car] def findAll(country: String)(implicit dbId: DBId): Seq[Car]}class DefaultAutoRepository extends AutoRepository { def find(regId: String)(implicit dbId: DBId): Option[Car] = { // ... } def findAll(country: String)(implicit dbId: DBId): Seq[Car] = { // ... }}

示例

// Anti patternclass CarFinder { val dbId = DbId("Dealer1") val repo = new DefaultAutoRepository def getCar(regId: String): Option[Car] = repo.find(regId)(dbId) def listCars(country: String): Seq[Car] = repo.findAll(country)(dbId)}

示例(续)

// Use implicits => much cleaner codeclass CarFinder { implicit val dbId = DbId("Dealer1") val repo = new DefaultAutoRepository def getCar(regId: String): Option[Car] = repo.find(regId) def listCars(country: String): Seq[Car] = repo.findAll(country)}

示例(续)

编译器练习

✦ 隐式作用域‣ 词法: 当前作用域、显式导入、通配符

导入‣ 部分类型的伴生对象: 类型的伴生对象

、参数类型的伴生对象、嵌套类型的外部对象

✦ 编译时可能会消耗大量资源,应谨慎使用

trait Logger { def log(msg: String) }object Logger { implicit object DefaultLogger extends Logger { def log(msg: String) = println("DL> " + msg) } def log(msg: String)(implicit logger: Logger) = { logger.log(msg) }}

隐式值

scala> Logger.log("a small test")DL> a small testscala> class MyLogger extends Logger { def log(msg: String) = println("ML:>> " + msg) }scala> implicit def myLogger = new MyLoggerscala> Logger.log("another test")ML:>> another test

隐式值

隐式智慧?

deech @deech调试 #scala 隐式内容如同在一个拥挤的房间中寻找 farter

类型特性

// "describes generic interfaces using type // parameters such that the implementations can// be created for any type"trait Encodable[T] { def from(t: T): String def to(s: String): T}object Encodable { implicit object IntEncoder extends Encodable[Int] { def from(i: Int): String = "int" + i def to(s: String): Int = s.substring(s.indexOf("int")+3, s.length).toInt }}

即,类型类

class MyHandler { def convert[T](t: T)(implicit enc: Encodable[T]): String = enc.from(t) def convert[T](s: String)(implicit enc: Encodable[T]): T = enc.to(s)}scala> val myHandler = new MyHandlerscala> myHandler.convert(12345)res0: String = int12345scala> myHandler.convert(res0)res1: Int = 12345

使用示例

scala> myHandler.convert(12345L)<console>:15: error: could not find implicit value for parameter encoder: Encodable[Long]scala> implicit object LongEnc extends Encodable[Long] { def from(l: Long): String = "long" + l def to(s: String): Long = s.substring(s.indexOf("long")+4, s.length).toLong }scala> myHandler.convert(12345L)

示例(续)

集合

集合概述

不可变集合

可变集合

// It is absolutely *awesome*scala> val seq = Seq()scala> seq.++ ++: +: /: /:\ :+ :\ addString aggregate andThen apply applyOrElse

asInstanceOf canEqual collect collectFirst combinations companion compose contains containsSlice copyToArray copyToBuffer

corresponds count diff distinct drop dropRight dropWhile endsWith exists filter filterNot find

flatMap flatten fold foldLeft foldRight forall foreach genericBuilder groupBy grouped hasDefiniteSize head

headOption indexOf indexOfSlice indexWhere indices init inits intersect isDefinedAt isEmpty isInstanceOf

isTraversableAgain iterator last lastIndexOf lastIndexOfSlice lastIndexWhere lastOption length lengthCompare lift map max

maxBy min minBy mkString nonEmpty orElse padTo par partition patch permutations

prefixLength product reduce reduceLeft reduceLeftOption reduceOption reduceRight reduceRightOption repr reverse reverseIterator

reverseMap runWith sameElements scan scanLeft scanRight segmentLength seq size slice sliding sortBy

sortWith sorted span splitAt startsWith stringPrefix sum tail tails take takeRight takeWhile

to toArray toBuffer toIndexedSeq toIterable toIterator toList toMap toSeq toSet toStream toString

toTraversable toVector transpose union unzip unzip3 updated view withFilter zip zipAll zipWithIndex

了解和学习 API

给 Java 开发人员的信息

✦ 使用 Vector 而不是 List

‣ 它更快‣ 它对内存的使用更高效

模式匹配

@scala.annotation.tailrecdef len[A](v: Vector[A], l: Int): Int = v match { case h :: t => len(t, l + 1) case Nil => l}scala> len(Vector("a","b","c","d")res0: Int = 4

FP 模式匹配

def convertedAge(a: Animal): Int = a match { case Dog(name, age) => age * 7 case Human(_, age, _) => age case Walrus("Donny", age) => age / 10 case Walrus(name, age) if name == "Walter" => age case _ => 0}

提取并进行“ instanceof”判断

相关成本

✦ 请注意,模式匹配实际上是 if - else if。

✦尝试在开始时放入最常见的发现,以减少跳跃次数

函数式编程

忠告

✦ 了解函数式编程的模式✦ 阅读有关 Functor 、 Applicative 和

Monad 的精彩文章‣ http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monad

s_in_pictures.html

✦查看 scalaz 了解 Scala 中 FP 的更多信息

值和上下文23

(+8)

函数

31

2323

值和上下文

2323

Some[Int] 无

选项

上下文

Functor

2323 (+8) 不适用!

2323

23 (+8) 31

3131

Some[Int] Some[Int]X (+8) X

无无

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

Functor 定义

def dblFunc(values: Vector[Int]): Vector[Int] = values map { _ * 2 } scala> dblFunc(Vector(1,2,3,4))res0: Vector(2,4,6,8)

Scala 中的 Functor

Applicative

2323

Some[Int]

(+8(+8))

Some[f(x)]

23(+8)

3131

Some[Int]

scala> Vector(1,2,3)res0: Vector[Int] = Vector(1,2,3)scala> List("A","B","C")res1: List[String] = List(A,B,C)scala> res0 zip res1res2: List[(Int,String)] = List((1,A),(2,B),(3,C))

Scala Applicative 示例

Monad

2323

Some[Int]

23

isEven

2424

24

4848

24

2424

Some[Int]

isEven

*2无

import scala.language.higherKindstrait Monad[M[_]] { def pure[A](a: A): M[A] def bind[A,B](ma: M[A])(f: A => M[B]): M[B]}

Monad 定义

def even(number: Option[Int]): Option[Int] = for { n <- number if n % 2 == 0 } yield nscala> even(Some(22))res1: Option[Int] = Some(22)scala> even(Some(21))res2: Option[Int] = None

Scala Monad 示例

总结

✦ FUNCTOR

- 将函数应用到包装值✦ APPLICATIVE

- 将包装函数应用到包装值✦ MONAD

- 将返回包装值的函数应用到包装值

推荐的 Scala 书籍

EOF@h3nk3

top related