Coding in Style J. Suereth Senior Software Engineer 粋なコード
Agenda ● The Scala mindset ● Scala 2.10 new features ● A brave new venture Scala 風に考える/Scala 2.10 の新機能/その先へ
Favor Expressions 文より式
def isAwesome(data: Data): Boolean = { if(data.awesome) return true if(data.lame) return false return data.contains("Tasty Sandwich") }
def isAwesome(data: Data): Boolean = if(data.awesome) true else if(data.lame) false else (data contains "Tasty Sandwich") }
def isAwesome(data: Data): Boolean = (data.awesome || (!data.lame) || (data contains "Tasty Sandwich"))
Embrace Operator Notation 演算子(中置)記法を取り入れよう
def privs(user: Option[User]): Privileges = user.map(findUserPrivileges).getOrElse( NoPrivs)
def privs(user: Option[User]): Privileges = (user map findUserPrivileges getOrElse NoPrivs)
Let the language do the work SUDO Make me a variable 作業を言語に任せる
def slurp(file : File): String = { var input: InputStream = null var output: OutputStream = null var read = 0 try { input = new FileInputStream(file) output = new StringOutputStream() val buf = new Array[Byte](BUF_SIZE) read = input.read(buf) while(read > 0) { output.write(buf, 0, read) read = input.read(buf) } } finally { if(input != null) input.close() if(output != null) output.close() } if(output != null) return output.toString return null }
def slurp(in: File): String = { val in = new FileInputStream(in) val out = new StringWriter() val buf = new Array[Byte](BUF_SIZE) def read(): Unit = (in read buf) match { case 0 => () case n => out.write(buf, 0, n) read() } try read() finally in.close() out.toString }
Tail-recursioN! @tailrec def read(): Unit = (in read buf) match { case 0 => () case n => out.write(buf, 0, n) read() }
Tail-recursioN! @tailrec def read(): Unit = (in read buf) match { Base Case case 0 => () case n => out.write(buf, 0, n) read() }
Tail-recursioN! @tailrec def read(): Unit = (in read buf) match { case 0 => () Recursive Case case n => out.write(buf, 0, n) read() }
If it seems useful, look for it 便利そうであれば、まずは探そう
def countEm(counts: Seq[Int]): Int = { var total = 0 for(count <- counts) total += count total }
def countEm(counts: Seq[Int]): Int = counts.sum
Collections! ● Scala collections have > 100 useful methods ● By far the biggest reason to use Scala is manipulating data using collections Scala を使う最大の理由: コレクションを使ったデータ処理
Aim Higher higher-order-functions, that is 上を狙おう (高階関数)
def sort[T]( data: Seq[T] )( lessThan: (T,T) => Boolean ): Seq[T] = ...
Why higher order functions? def sort[T]( data: Seq[T] )( lessThan: (T,T) => Boolean ): Seq[T] = ... Abstract the algorithm (how to do something) from the goal (what to do) 目的(何がしたいか) とアルゴリズム(どうやるか) を分離する
New w/ Scala 2.10 Features that alter your daily code Scala 2.10 の新機能
Build strings the nice way interpolators 文字列生成を楽に
val HAI = "HI" val name = "SQUARE" val height = 100 val width = 20 scala> val x = s"${HAI}" x: String = HI scala> f"$name is $height%04d meters by $width%04d meters" res0: String = SQUARE is 0100 meters by 0020 meters
Keep the abstract.... .... well abstract. 抽象的なものは抽象的に
A Graph Library API trait Node[N] { trait Graph[N,E] { def value: N type Nd = Node[N] } type Ed = Edge[N,E] trait Edge[N,E] { def value: E def nodes: Set[Nd] def from: Node[N] def edges(n: Nd): Seq[Ed] def to: Node[N] } }
A Graph Library API trait Node[N] { trait Graph[N,E] { def value: N type Nd = Node[N] } Great for implementers, type Ed = Edge[N,E] trait but where's all the Edge[N,E] { def friendly user methods? Set[Nd] value: E def nodes: def from: Node[N] def edges(n: Nd): Seq[Ed] def to: Node[N] } } 実装する人にはいいけど、trait を使う人にやさしくない
Add behavior with shiny new value classes and implicit classes 値クラスとimplicit クラスで振る舞いを追加する
object Graph { implicit class Ops[N,E](val g: Graph[N,E]) extends AnyVal { def isAcyclic: Boolean = cycles forall (c => (c drop 1). isEmpty) def cycles: Set[Set[Node[N]]] = ... compute using tarjan or other algorithm ... } }
object Graph { implicit class Ops[N,E](val g: Graph[N,E]) extends AnyVal { def isAcyclic: Boolean = cycles forall (c => (c drop 1). isEmpty) def cycles: Set[Set[Node[N]]] = ... compute using tarjan or other algorithm ... } }
Fast at runtime, too val graph: Graph[N, E] = ... if(graph.isCyclic) sys.error("O NOES") Translates to static helper method call val graph: Graph[N, E] = ... if(Graph.Ops.isCyclic(graph)) sys.error("O NOES") 実行時には static メソッドの呼び出しへと変換される
Cheat in an Emergency With Dynamic Types いざという時の動的型
class MapHelper(json: Map[String,AnyRef]) extends Dynamic { def selectDynamic(name: String) = (json get name getOrElse sys.error(s"Attribute $name not found in json: $json")) }
scala> val m = new MapHelper(Map[String, AnyRef]("name" -> "Jimmy", "age" -> "10")) m: MapHelper = MapHelper@683f67e0 scala> m.name res1: AnyRef = Jimmy scala> m.sex java.lang.RuntimeException: Attribute sex not found in json: Map(name -> Jimmy, age -> 10)
Promise the Future! Make sure to deliver Promise とFuture (約束は守りましょう)
import scala.concurrent._ import scala.concurrent.ExecutionContext. Implicits.global scala> val x = promise[Int] scala> val y = x.future scala> y.isCompleted res5: Boolean = false scala> x success 5 scala> y.isCompleted res7: Boolean = true scala> y.value res9: Option[Either[Throwable,Int]] = Some (Right(5))
Why not Java Futures? future { executor.submit( Thread sleep 30000L 5 new } Callable<Integer>( ) { def call(): Creating! Integer = { Thread sleep 3000L new Integer(5) }
Why not Java Futures? val x: Future[Int] = val x: Future[Integer] ... = ... Await.result(x, 10 seconds) x.get(10, Blo TimeUnit.SECONDS) cki ng !
Why not Java Futures? val x: Future[Int] = N/A ... val y: Future[Int] = x map { result => result + 5 king } l oc -B ! on ins N a ch
Scala's Futures Provide a lightweight model for asynchronous, non-blocking execution that can be chained and joined throughout your application. Future: 軽量な非同期・ノンブロッキング実行モデル
Staying Async! See http://jsuereth.com/intro-to-fp/ いつも非同期で
trait AsynchService { def users: Future[Seq[User]] def projects(user: User):Future[Seq[Project]] }
def allprojects(api: AsynchService): Future[Set [Project]] = (Future.traverse(api.users)(api.projects) map (_.flatten.toSet))
Traverse is fork join def traverse[A]( seq: Future[Seq[A]] )( op: A => Future[B] ): Future[Seq[B]] = .... traverse は fork/join を用いて行われる
Feeling Crazy? Manipulate some ASTs ちょっと遊ぶ?AST をいじろう
object MacroAsserts { def assert(cond: Boolean, expr: String) = macro assertImpl def assertImpl(c: Context)( cond: c.Expr[Boolean], msg: c.Expr[String] ): c.Expr[Unit] = if(sys.env("SCALA_ASSERT") == "true") c.reify(if(cond.splice) sys.error(msg.splice)) else c.reify(()) }
object MacroAsserts { What the user sees def assert(cond: Boolean, expr: String) = macro assertImpl def assertImpl(c: Context)( cond: c.Expr[Boolean], msg: c.Expr[String] ): c.Expr[Unit] = if(sys.env("SCALA_ASSERT") == "true") c.reify(if(cond.splice) sys.error(msg.splice)) else c.reify(()) }
object MacroAsserts { def assert(cond: Boolean, expr: String) = macro assertImpl def assertImpl(c: Context)( cond: c.Expr[Boolean], msg: c.Expr[String] ): c.Expr[Unit] = if(sys.env("SCALA_ASSERT") == "true") c.reify(if(cond.splice) sys.error(msg.splice)) else c.reify(()) } What the compiler uses
Tree Transformation object Bar { assert(false, "THIS IS A BAD OBJECT") } 構文木の変換
object Bar { Expr[Boolean] } Expr[Any]
object Bar { Macro Expr[Boolean] } Expr[Any]
object Bar { Expr[Unit] Macro }
object Bar { () }
A Brave New World Welcome to すばらしい新世界へようこそ
What's coming in Scala 2.11? Nothing certain, but a list of possibilities ● Smaller ○ Modularization of components? ○ Eviction of deprecated libraries? ● Faster ○ More reliable inlining? ○ Method-Handle based closures? ● Stabler ○ Fix ALL THE BUGS ○ Reflection API Stabilizes? より小さく、高速で、安定した Scala 2.11
Resources Collections: http://docs.scala-lang.org/overviews/collections/introduction.html String Interpolation: http://docs.scala-lang.org/overviews/core/string- interpolation.html Value Classes: http://docs.scala-lang.org/overviews/core/value-classes.html Futures: http://docs.scala-lang.org/overviews/core/futures.html Reflection: http://docs.scala-lang.org/overviews/reflection/overview.html Macros: http://docs.scala-lang.org/overviews/macros/overview.html
Questions? 質問ある?

Coding in Style

  • 1.
    Coding in Style J. Suereth Senior Software Engineer 粋なコード
  • 2.
    Agenda ●The Scala mindset ● Scala 2.10 new features ● A brave new venture Scala 風に考える/Scala 2.10 の新機能/その先へ
  • 3.
  • 4.
    def isAwesome(data: Data):Boolean = { if(data.awesome) return true if(data.lame) return false return data.contains("Tasty Sandwich") }
  • 5.
    def isAwesome(data: Data):Boolean = if(data.awesome) true else if(data.lame) false else (data contains "Tasty Sandwich") }
  • 6.
    def isAwesome(data: Data):Boolean = (data.awesome || (!data.lame) || (data contains "Tasty Sandwich"))
  • 7.
  • 8.
    def privs(user: Option[User]):Privileges = user.map(findUserPrivileges).getOrElse( NoPrivs)
  • 9.
    def privs(user: Option[User]):Privileges = (user map findUserPrivileges getOrElse NoPrivs)
  • 10.
    Let the languagedo the work SUDO Make me a variable 作業を言語に任せる
  • 11.
    def slurp(file :File): String = { var input: InputStream = null var output: OutputStream = null var read = 0 try { input = new FileInputStream(file) output = new StringOutputStream() val buf = new Array[Byte](BUF_SIZE) read = input.read(buf) while(read > 0) { output.write(buf, 0, read) read = input.read(buf) } } finally { if(input != null) input.close() if(output != null) output.close() } if(output != null) return output.toString return null }
  • 12.
    def slurp(in: File):String = { val in = new FileInputStream(in) val out = new StringWriter() val buf = new Array[Byte](BUF_SIZE) def read(): Unit = (in read buf) match { case 0 => () case n => out.write(buf, 0, n) read() } try read() finally in.close() out.toString }
  • 13.
    Tail-recursioN! @tailrec defread(): Unit = (in read buf) match { case 0 => () case n => out.write(buf, 0, n) read() }
  • 14.
    Tail-recursioN! @tailrec defread(): Unit = (in read buf) match { Base Case case 0 => () case n => out.write(buf, 0, n) read() }
  • 15.
    Tail-recursioN! @tailrec defread(): Unit = (in read buf) match { case 0 => () Recursive Case case n => out.write(buf, 0, n) read() }
  • 16.
    If it seemsuseful, look for it 便利そうであれば、まずは探そう
  • 17.
    def countEm(counts: Seq[Int]):Int = { var total = 0 for(count <- counts) total += count total }
  • 18.
  • 19.
    Collections! ● Scalacollections have > 100 useful methods ● By far the biggest reason to use Scala is manipulating data using collections Scala を使う最大の理由: コレクションを使ったデータ処理
  • 20.
    Aim Higher higher-order-functions, that is 上を狙おう (高階関数)
  • 21.
    def sort[T]( data: Seq[T] )( lessThan: (T,T) => Boolean ): Seq[T] = ...
  • 22.
    Why higher orderfunctions? def sort[T]( data: Seq[T] )( lessThan: (T,T) => Boolean ): Seq[T] = ... Abstract the algorithm (how to do something) from the goal (what to do) 目的(何がしたいか) とアルゴリズム(どうやるか) を分離する
  • 23.
    New w/ Scala2.10 Features that alter your daily code Scala 2.10 の新機能
  • 24.
    Build strings thenice way interpolators 文字列生成を楽に
  • 25.
    val HAI = "HI" val name = "SQUARE" val height = 100 val width = 20 scala> val x = s"${HAI}" x: String = HI scala> f"$name is $height%04d meters by $width%04d meters" res0: String = SQUARE is 0100 meters by 0020 meters
  • 26.
    Keep the abstract.... .... well abstract. 抽象的なものは抽象的に
  • 27.
    A Graph LibraryAPI trait Node[N] { trait Graph[N,E] { def value: N type Nd = Node[N] } type Ed = Edge[N,E] trait Edge[N,E] { def value: E def nodes: Set[Nd] def from: Node[N] def edges(n: Nd): Seq[Ed] def to: Node[N] } }
  • 28.
    A Graph LibraryAPI trait Node[N] { trait Graph[N,E] { def value: N type Nd = Node[N] } Great for implementers, type Ed = Edge[N,E] trait but where's all the Edge[N,E] { def friendly user methods? Set[Nd] value: E def nodes: def from: Node[N] def edges(n: Nd): Seq[Ed] def to: Node[N] } } 実装する人にはいいけど、trait を使う人にやさしくない
  • 29.
    Add behavior with shiny new value classes and implicit classes 値クラスとimplicit クラスで振る舞いを追加する
  • 30.
    object Graph { implicit class Ops[N,E](val g: Graph[N,E]) extends AnyVal { def isAcyclic: Boolean = cycles forall (c => (c drop 1). isEmpty) def cycles: Set[Set[Node[N]]] = ... compute using tarjan or other algorithm ... } }
  • 31.
    object Graph { implicit class Ops[N,E](val g: Graph[N,E]) extends AnyVal { def isAcyclic: Boolean = cycles forall (c => (c drop 1). isEmpty) def cycles: Set[Set[Node[N]]] = ... compute using tarjan or other algorithm ... } }
  • 32.
    Fast at runtime,too val graph: Graph[N, E] = ... if(graph.isCyclic) sys.error("O NOES") Translates to static helper method call val graph: Graph[N, E] = ... if(Graph.Ops.isCyclic(graph)) sys.error("O NOES") 実行時には static メソッドの呼び出しへと変換される
  • 33.
    Cheat in anEmergency With Dynamic Types いざという時の動的型
  • 34.
    class MapHelper(json: Map[String,AnyRef]) extends Dynamic { def selectDynamic(name: String) = (json get name getOrElse sys.error(s"Attribute $name not found in json: $json")) }
  • 35.
    scala> val m= new MapHelper(Map[String, AnyRef]("name" -> "Jimmy", "age" -> "10")) m: MapHelper = MapHelper@683f67e0 scala> m.name res1: AnyRef = Jimmy scala> m.sex java.lang.RuntimeException: Attribute sex not found in json: Map(name -> Jimmy, age -> 10)
  • 36.
    Promise the Future! Make sure to deliver Promise とFuture (約束は守りましょう)
  • 37.
    import scala.concurrent._ import scala.concurrent.ExecutionContext. Implicits.global scala>val x = promise[Int] scala> val y = x.future scala> y.isCompleted res5: Boolean = false scala> x success 5 scala> y.isCompleted res7: Boolean = true scala> y.value res9: Option[Either[Throwable,Int]] = Some (Right(5))
  • 38.
    Why not JavaFutures? future { executor.submit( Thread sleep 30000L 5 new } Callable<Integer>( ) { def call(): Creating! Integer = { Thread sleep 3000L new Integer(5) }
  • 39.
    Why not JavaFutures? val x: Future[Int] = val x: Future[Integer] ... = ... Await.result(x, 10 seconds) x.get(10, Blo TimeUnit.SECONDS) cki ng !
  • 40.
    Why not JavaFutures? val x: Future[Int] = N/A ... val y: Future[Int] = x map { result => result + 5 king } l oc -B ! on ins N a ch
  • 41.
    Scala's Futures Providea lightweight model for asynchronous, non-blocking execution that can be chained and joined throughout your application. Future: 軽量な非同期・ノンブロッキング実行モデル
  • 42.
    Staying Async! See http://jsuereth.com/intro-to-fp/ いつも非同期で
  • 43.
    trait AsynchService { def users: Future[Seq[User]] def projects(user: User):Future[Seq[Project]] }
  • 44.
    def allprojects(api: AsynchService):Future[Set [Project]] = (Future.traverse(api.users)(api.projects) map (_.flatten.toSet))
  • 45.
    Traverse is forkjoin def traverse[A]( seq: Future[Seq[A]] )( op: A => Future[B] ): Future[Seq[B]] = .... traverse は fork/join を用いて行われる
  • 47.
    Feeling Crazy? Manipulate some ASTs ちょっと遊ぶ?AST をいじろう
  • 48.
    object MacroAsserts { def assert(cond: Boolean, expr: String) = macro assertImpl def assertImpl(c: Context)( cond: c.Expr[Boolean], msg: c.Expr[String] ): c.Expr[Unit] = if(sys.env("SCALA_ASSERT") == "true") c.reify(if(cond.splice) sys.error(msg.splice)) else c.reify(()) }
  • 49.
    object MacroAsserts { What the user sees def assert(cond: Boolean, expr: String) = macro assertImpl def assertImpl(c: Context)( cond: c.Expr[Boolean], msg: c.Expr[String] ): c.Expr[Unit] = if(sys.env("SCALA_ASSERT") == "true") c.reify(if(cond.splice) sys.error(msg.splice)) else c.reify(()) }
  • 50.
    object MacroAsserts { def assert(cond: Boolean, expr: String) = macro assertImpl def assertImpl(c: Context)( cond: c.Expr[Boolean], msg: c.Expr[String] ): c.Expr[Unit] = if(sys.env("SCALA_ASSERT") == "true") c.reify(if(cond.splice) sys.error(msg.splice)) else c.reify(()) } What the compiler uses
  • 51.
    Tree Transformation objectBar { assert(false, "THIS IS A BAD OBJECT") } 構文木の変換
  • 52.
    object Bar { Expr[Boolean] } Expr[Any]
  • 53.
    object Bar { Macro Expr[Boolean] } Expr[Any]
  • 54.
    object Bar { Expr[Unit] Macro }
  • 55.
  • 56.
    A Brave NewWorld Welcome to すばらしい新世界へようこそ
  • 57.
    What's coming inScala 2.11? Nothing certain, but a list of possibilities ● Smaller ○ Modularization of components? ○ Eviction of deprecated libraries? ● Faster ○ More reliable inlining? ○ Method-Handle based closures? ● Stabler ○ Fix ALL THE BUGS ○ Reflection API Stabilizes? より小さく、高速で、安定した Scala 2.11
  • 58.
    Resources Collections: http://docs.scala-lang.org/overviews/collections/introduction.html String Interpolation:http://docs.scala-lang.org/overviews/core/string- interpolation.html Value Classes: http://docs.scala-lang.org/overviews/core/value-classes.html Futures: http://docs.scala-lang.org/overviews/core/futures.html Reflection: http://docs.scala-lang.org/overviews/reflection/overview.html Macros: http://docs.scala-lang.org/overviews/macros/overview.html
  • 59.