たのしい Scala | サイバーエージェント 公式エンジニアブログ

はじめに

初めまして。
2011年度入社のつちはしと申します。
アメーバのゲーム部門でエンジニアをしています。
今回はエンジニアブログを書く機会を頂きましたので、大好きな Scala について書かせていただきました。

「たのしさ」というとらえどころのない話しゆえ、すこしゆるーくなっておりますが、ご了承くださいませ。

というわけで、よく「Ruby は使っていて楽しいお 気持ちいいお」と聞くけど、
Scala も楽しいし気持ちいいんだよー!

とゆうのを伝えたいです。。 伝わるといいです。。

(この記事は私が Scala の楽しいと感じる部分に的を絞って書いています。
Scala には楽しくない部分もいろいろありますが、それに関してはここでは触れません。
Scala たんは俺の嫁)

さくっと書いてためしてみることが出来るの

本題に入る前に。。
Ruby には irb という、その場でプログラムを書いてすぐに試せるツールがありますが、 Scala にも似たようなものがあるんです。
Scala REPL といいます

Scala が入っている環境なら、コマンドラインから

$ scala

と入力すれば起動しますお。早速何か入力して、エンター押してみましょう~
println("hello world.")

mac で brew だったら
brew install scala

で Scala が入るので、ここから一緒に試してみましょうお

なにが楽しいのかな

というわけで、本題です~

Scala は何が楽しいのかな?
うーん、うーん、と考えてみました
きっと楽しさは人それぞれなので、私の主観をたくさん含みつつ、以下の5つに絞ってみました
  • マスコットがかわいい
  • やりたいことが素直に書ける
  • 言語仕様にわくわくできる
  • 言語を拡張できる
  • すてきな環境

いろいろありそうですが、私はこの5つかなーと

順番に見ていきますお~

■「マスコットがかわいい」

画像のライセンスが不明なので、リンクを。。
http://subtech.g.hatena.ne.jp/secondlife/20090701/1246418689
あらかわいい

真夜中3時に障害対応していたって、この子の可愛さでがんばれますね(*ノ∀ノ)

■「やりたいことが素直に書ける」


プログラミングは本来楽しいものだと思うのです
けど、書きたいものと実際に書くものの間に乖離があるほど、楽しくなくなっていくと思うのですお
やりたいことをやろうとしたら、ちょっと邪魔が入った。みたいな感じでしょうか
考えるままに書けると、とても気持ちがいいものですよね

  • 今までの考え方がそのまま使えること
  • それがよりシンプルに実現できること

この2つかなぁと思うのです

Scala はこれらを満たしていると思うんです~
例を見ていきますね

コレクション操作の考え方

たとえば Ruby のコレクション操作はとても気持ちいいです
それは、 Ruby のコレクション操作の考え方に慣れ親しんでいるからで、
それがそのまま Scala でも使えたら、気持ちいいと思うんです

Scala でのコレクション操作は、こんな感じです

List(1, 2, 3).map(i => i * 2).foreach(i => println(i))

> 2
> 4
> 6

(*´Д`) あら気持ちいい・・

Scala の場合、もうちょっと自明のものを削ることも出来ますお
List(1, 2, 3).map(_ * 2).foreach(println)


文字列操作の考え方


Ruby は文字列操作も気持ちよくできます

Scala も気持ちがいいんですお
"hello_scala_world".split("_").map(s => s.capitalize).mkString("")
> HelloScalaWorld

(*´Д`)

値としての関数という考え方

(ここは関数型言語の考え方を知っている人じゃないと、少し難しいかもしれません。
関数型言語の考え方も Scala ではそのまま使える例として載せています。)

関数型言語では関数を値として扱うことができます
上のコレクション操作でも、関数を値として関数に渡していました

Scala の場合はこんなかんじですお~

// func という関数を受け取り呼び出す関数
def callCallback(func: String => Any) = {
  func("callback!!")
}


// print するだけの関数
def callback(s: String) = println(s)


// callCallback に callback を引数として渡す
callCallback(callback)
> callback!!

代数的データ型とパターンマッチという考え方

(ここは関数型言語の考え方を知っている人じゃないと、少し難しいかもしれません。
関数型言語の考え方も Scala ではそのまま使える例として載せています。)

関数型言語では代数的データ型というものを使うらしいですお

Wikipedia  の例を Scala で書くと、こんな感じになりますお


  sealed trait Node


  final case class Leaf(l: Int) extends Node


  final case class Branch(a: Node, b: Node) extends Node


  def depth(tree: Node): Int = tree match {
    case Leaf(_) => 1
    case Branch(a, b) => 1 + depth(a).max(depth(b))
  }


  val tree = Branch(Branch(Leaf(1), Leaf(2)), Branch(Leaf(3), Leaf(4)))
  depth(tree)


> 3

(´;ω;`) うーん、オブジェクト指向に慣れ親しんだ身としてはわかりやすいですが、 Haskell と比べると冗長で見劣りしてしまう。。

こういうのをみると、 Scala はオブジェクト指向言語に関数型の考え方を取り入れたものなんだなぁ。。
という気がしてきます。 あくまでオブジェクト指向が主で、関数型の考え方でも組めますよ。というスタンスを感じます

余談ですが、この書き方は Visitor パターンの代わりとしても使えますね

よりシンプルな DTO

Java で DTO を作るとき、 getter や setter を定義した100行くらいのコードを書くことがよくあります

Scala はこれをより容易にしてくれます

case class User(
  name: String,
  age: Int)

こう書くことで getter, setter, コンストラクタ, clone みたいなもの等が定義されます~
Lombok  みたいですね

モナ・・・

あいつはボクには難しすぎるお。。。

■ 「言語仕様にわくわくできる」

次に、「言語仕様にわくわくできる」と楽しいなーと私は感じます
言葉足らずでごめんなさいなのですが、
私は、統一感があって、しっくりくると「すごいなー たのしいなー」
と感じるのだと思います

幾つか例をみていきます~

データの生成の一般化

多くの言語では配列や連想配列等、組み込みのデータ構造が特別扱いされていたりします
新しいデータ構造を作った場合、組み込みのデータ構造とは別の書き方をしないといけなかったりもします

Scala ではデータの作り方が統一されています。
  val list = List(1, 2, 3)
  
  val array = Array(1, 2, 3)


  val map = Map(
    "モナド" -> "わかんない",
  )

自分でデータ構造を作るときも、同じように作ることができるので、
統一感があるなーとかんじますお

null の扱いの一般化

NULL に初めてであったのは C/C++ 言語でした
あの時から、私の悩みの種は尽きません
null ってなに? 0 なの? ポインタなの? なんなの!? またぬるぽ起きたし!

考えてみると、この定義は謎です。
  val foo: Foo = null
Foo 型の foo に null が代入できるのであれば、 null の型は Foo 型のサブタイプでなければなりません。
じゃあ、 null の型はなあに?

Scala はこの点、少しだけ頑張っています
null の型は Null であり、 Null はあらゆる参照型のサブタイプと定義されています


Null


Null は親クラスのメソッドをオーバーライドしており、
実行するとほとんどのメソッドが NullPointerException を投げるようになっています

null.toString
> java.lang.NullPointerException

「すべてはオブジェクトである」という考えを頑張って実現しようとしている努力が可愛い Scala たん。。(*´Д`)ハァハァ


■「言語を拡張できる」

それと「言語を拡張できる」こと
これがとてもたのしいです!

  • プロジェクト固有の構文を定義できたり
  • 組み込み型にプロジェクト固有の処理を追加できたり

気軽に DSL みたいなものが作れそうですねー!
(やり過ぎるとマサカリが飛んでくるのできおつけるのお。。)

演算子の定義

Scala の演算子は、実際はメソッド呼び出しです

例えばこんなのがあったとすると。。
1 + 2
これはこんなメソッド呼び出しと同じです
1.+(2)

つまり、演算子を自分で作ることができます

case class Vector2(x: Double, y: Double) {
  def +(v: Vector2) =
    Vector2(this.x + v.x, this.y + v.y)
}


Vector2(1, 2) + Vector2(3, 4)
> Vector2(4.0,6.0)

制御構造の定義

Ruby で見かける 3.times do xxx end
こういうのを自分で作ることができます。

例えば、例外を無視する構文
import scala.util.control.NonFatal


def ignoreException(f: => Any) {
  try {
      f
  } catch {
    case NonFatal(e) =>
  }
}


ignoreException {
  println("hi!")
  throw new Exception
}
> hi!

標準ライブラリに面白い例がいろいろ入っているので、みてみるとたのしいですおー!
- Actor
- Try

組み込みクラスにメソッドを追加

Java 以外の様々な言語で、組み込みクラスやライブラリのクラスにメソッドを追加することができます

Scala の場合はこんな感じですお

implicit class MyString(val s: String) extends AnyVal {
  def hello = s"Hello! $s こんにちは!"
}


"よっしー".hello
> Hello! よっしー こんにちは!

Java の String には、似た方法で Scala 独自の便利なメソッドがたくさん追加されていますお

for 式

Scala の for 式はとてもとてもおもしろいのです!

Ruby 等と同様、 for 式はシンタックスシュガーで、
対象のインスタンスの map, flatMap, filter, foreach 等のメソッド呼び出しに置換されます~
つまり、 for の実際の意味はこれらのメソッドによって定義できるということ!

そして、 Scala の for はループのためだけのシンタックスシュガーではなく、さらに広い利用範囲をもっているのです。

これを使ったライブラリはこんなものがあります。
- Future と Promise
  非同期処理の連鎖を for 式でシンプルに記述できます

マクロ

これを使用すると、コンパイル時にプログラムを生成することができます
Scala のプログラムをパースした結果をプログラムで処理し、新しいプログラムを作ることができます
マジック!

これを使ったライブラリにはこんなものがあります

- SLICK の direct embedding
  Scala のプログラムから SQL を生成できます
- ScalaLogging
  ログを出さない時はログ生成コードを一切実行しないようにできる、ログライブラリです

すてきな環境

最後に、言語のたのしさは環境によるところも大きいです
すてきな Vim 拡張があったりすると、テンション上がりますよねー!

Scala の場合は IntelliJ IDEA  に Scala プラグインを入れて準備は完了!
(Eclipse や Vim もあるけど、 私は IDEA が好き!)

考えるままにキーを叩くと、それを正確な補完でサポートしてくれる IDE がとてもいいやつにおもえます。

ライブラリの海に潜っていこう

困ったときは IDE で「定義に飛んで」みましょう!
Scala の標準ライブラリだって、誰かの作ったライブラリだって、すぐにソースとドキュメントが読めますお。
(Scala に限らないけど) 使っているライブラリのソースをシームレスに参照できる環境ってすてきです~

最後に


長々と書いてきましたが、 Scala の楽しさがすこしでも伝わったら嬉しいです(*´∀`)

ぜひ一度 Scala を触ってみて、それがきっかけで、日本に、社内に Scala 好きな人が増えると嬉しいです~
(言語の選定をできる立場の方は、まずは自分で触ってみて欲しいのです。 Scala は Java の資産も使えますよ~)

ありがとうございました!