Scalaメモ

  • 投稿日:
  • 更新日:2015/03/06
  • by
  • カテゴリ:

ちょっと最近気になる関数型言語、Scalaのメモ。

Overview

Scalaは、Javaとの親和性が高い静的型付き関数型言語で、2001年から開発されています。 開発元はこちらです。

関数型かつ純粋オブジェクト指向なので、Javaで苦手(不可能)とする以下のようなことをサポートしています。

  • プリミティブ型および関数のオブジェクトとしての扱い
  • staticの排除(代わりにシングルトンオブジェクトのネイティブサポート)
  • カリー化(currying)および高階関数(higher-order function)
  • 型推論機構
  • 遅延評価
  • trait (一種のmix-in)
    Javaのinterfaceに近いのですが、実装を持つことができます。
  • すべての識別子の中間演算子としての使用(a.func(b)はa func bと同じ、a + bはa.+(b)と同じ)
  • (配列とは異なる)リストのサポート
    配列とは、リストはimmutable(変更できない)、再帰的(内部にリストを含められる)、Lispのリストのようにさまざまなオペレーションがある、という点で違いがあります。
  • XML構文をサポート(プログラムの一部としてXMLが記述できる)
    たとえば、XMLを返す次のような関数が定義できます(インデントは見やすくするため)。 関数型言語なので、実行時の最後の評価値が返り値になり、明示的にreturnを使う必要はありません。
    def elm(attr: Any, content: Any) = {
      <data attr="{attr.toString}">
        {content}
      </data>
    }
    

これらの特徴により、プログラムをJavaにそっくりに書くこともできれば、関数型ばりばりのフォーマットで書くこともできます。Javaとのインタラクションを言語レベルでサポートしているので、Javaのプログラムをそのまま呼び出すことも可能です。 また、genericsなどJava 1.5+でサポートされた(無理のある)syntactic sugarが自然に書けるのもいいところです。

業務アプリへの適用可能性

関数型言語はある種の問題に対して非常にソリューションを書きやすいのですが、OCamlやHaskellなど、いままで業務の世界などであまり成功しているとはいえません。 その理由は、

  • 業務の世界は保守的なので新しい言語などの導入には慎重
  • 業務上、関数型のソリューションを必要とする場面が少ない

といったところでしょうか。

たとえば、RubyもRuby on Railsでそれなりの支持を得ていますが、メインストリームといえるほどの盛り上がりにはなっていないと思います。

Scalaの場合、Javaをネイティブサポートしていたり、普通に手続き型風にコードを書くことも可能だったり(構文も似ている)、結構気を使っている気がします。 ライセンスもBSDスタイルなので、それほど神経質になる必要がありません。

Javaのサポートについては、そもそもScalaプログラムをコンパイルすると、Javaのクラスになり、Scalaのライブラリを使って実行するようになっています。 加えて、Javaに「あったらいいな」と思える機能があるので、なかなか使えそうな気はします。

個人的には、プリミティブや関数をfirst class objectにしたり、高階関数が使えたりするのはとても便利です。JavaScriptでもできますし。 それと、静的型付け言語でコンパイル時に型チェックが行われるというのが、業務アプリの世界ではなじみやすいかもしれません。 RubyやJavaScriptなどの動的型付け言語は便利ですが、検証やテストが大変なので。

気になる点

ちょっと気になるのは、関数型言語の特徴を生かして書きたい場合、処理を再帰で書くことが多いのですが、メモリ効率や実行効率がどうなるかです。 たとえば、次のような例があります。

整数aとb(a <= b)の間の数の総和を求める。

def sumInts(a: Int, b: Int): Int = if (a > b) 0 else a + sumInts(a + 1, b)
  

この関数sumIntsは、aとbの差が大きければ再帰しまくるので、スタックを使っているとすればあっという間にあふれてしまいます。 tail recursionくらいは展開しているかもしれませんが...。

次は、クイックソートの例です。

def sort(xs: Array[Int]): Array[Int] =
  if (xs.length <= 1) xs
  else {
    val pivot = xs(xs.length / 2)
    Array.concat(
      sort(xs filter (pivot >)),
           xs filter (pivot ==),
      sort(xs filter (pivot <)))
  }
  

返り値の配列は引数とは異なるので、元の配列を保存する必要がない場合、余計にメモリを必要とします。 まぁ、これらが問題になるような場合には手続き的に書くことも可能なのですが。

開発環境

開発環境に関しては、Eclipseのプラグインが公開されており、Javaと同じような感覚で開発ができます。

Scalaの実行形式はJavaのバイトコードなので、デバッガを使うと、Scalaプログラム(ライブラリ含む)とJavaの間を行ったりきたりしながら実行されます。 Scalaで書かれた部分はScalaのコードとして表示されるため、きわめてシームレスなデバッグが可能です。

いずれにしても、これからどのようになっていくか、ちょっと楽しみです。

こちらもよく読まれています