Scala覚え書き その0 Windowsへのインストールと開発環境構築

Scalaを試した過程を淡々と記録していきます。

まず環境の構築。最近はMeadow上でruby-modeとかhaskell-modeとかを使って、インタプリタを呼びだしつついじるのが好きなので、Windowsへのインストール方法とscala-modeを探す。両方あっさりみつかった。

ダウンロードサイト
http://www.scala-lang.org/downloads/index.html
scala-modeを使ってみた人の記録
http://d.hatena.ne.jp/riue/20080105/1199552108

ダウンロードサイトより、IzPack Java Installerを落としてインストール。scala-mode.elは同梱されていたので、適当な場所にコピって.emacsにこんなふうに設定を書いて環境構築終了。

(setq load-path (cons "~/lib/elisp/scala" load-path))
(require 'scala-mode-auto)
(setq scala-interpreter "C:/bin/scala/bin/scala.bat"))

コンパイラを使うなら、コンパイラのある場所にパスを通したほうがいいんだろうけどまだいいや。

Scala覚え書き その1 HelloWorldと関数定義と無名関数

HelloWorld

JRubyな高井さんのサイトに本家のチュートリアルの写経シリーズ(http://recompile.net/2007/07/a-scala-tutorial-for-java-prog.html)があったので、HelloWorldを試す。emacs上にコピペして、run-scalaしてC-cC-l。無事にHelloWorldが出力された。

//class HelloWorld {  // クラス宣言
object HelloWorld {
  def main(args : Array[String]) {
    println("Hello, World")
  }
}
HelloWorld.main(null) // インタプリタで動かすためのコード

*1
このHelloWorldは"object宣言"で作られている。object宣言を使うとHelloWorldクラスを定義した上に、そのクラスのシングルトンオブジェクトを生成して、"HelloWorld"という名前と結びつけるらしい。HelloWorldはすでにオブジェクトなのでnewできない。class宣言というのもあって、それを使うとnewできるらしい。
元々のチュートリアルのコードには最終行は存在していない。コンパイルしてJavaのclassファイルを作った後にHelloWorldクラスを呼びだすとmain関数が呼ばれるらしい。最終行は、インタプリタに読みこませた時に出力されるように追加したもの。

じゃあクラス宣言にしたらこれでは動かないのかな、と思いきや、object宣言をclass宣言に置き変えても、HelloWorld.main(null)で無事に動いてしまった。class宣言だと、「HelloWorld」というのはインスタンスじゃなくてクラスになると思うんだけど、クラスからでもmainはそのまま呼べるのかな。インスタンスメソッドも呼べる仕様なのか、mainはインスタンスメソッドではないのか、それともクラスとインスタンスの境目があいまいなんだろうか。まだ謎。

関数定義

クラス定義やオブジェクト定義(?)をしなくても、べたにdefで関数定義ができるらし。というわけでお約束の階乗。

def fact(n:Int):Int = {
  if (n == 1) {1} else {(n * fact(n-1))}
}

"(n:Int):Int"なあたりは引数と関数(の返り値)の型を宣言している。Scalaはある程度型推論をやってくれて、返り値の型は省略しても推論してくれる。引数の型は省略するとエラーになってしまった。残念。上の例で返り値の型まで書いてあるのは、再帰関数だと返り値の型も書かなきゃいけないようだから。これまた残念。
あとちょっと変わってるのが関数定義の構文。中括弧を多用するところはCに似てるので、Cのように関数シグネチャの後に直接{}って書いてたらパースエラーになった。"= {}"にしないといけないようだ。このへんの感覚は、無名関数をつくってから変数と結びつける雰囲気で関数型ぽい。
{}の中はどう書くか、というとこれはschemeruby風。複数の式を羅列することができて、順番に実行される。最後の式の値が全体の値として返る。最後の式以外は副作用のある式を書くことになるんだろうな。きっと。

無名関数

じゃあ無名関数は、というと、これは"=>"で作れる。無名関数を作ってから変数に代入してみたりした。

def add(a:Int,b:Int) = {a+b}
val add2= {(a:Int,b:Int) => a + b}
val add3:Int=>Int=>Int = {a => b => a + b}

add3はカリー化されてるかんじか?変数の型は無名関数の入力に書いても、変数側にまとめて書いてもよい。けっこう柔軟。
ついでに変数の話も。valっていうのは変数の宣言なんだけど、もう一つvarってのもある。valは更新不能でvarは更新可能らしい。

val id = {n:Int=>n}
var id2 = {n:Int=>n}
// id = {n:Int=>n}  //これはエラー
id2 = {n:Int=>n+1}

valを使ったidへの再代入である3行目はエラーになっちゃうけど、varを使ったid2のほうは関数も更新できた。型はあってないと型エラーといわれてしまうけど、型さえあってりゃ関数の中身を書きかえられる。関数の中身を書き換えられるのに型のチェックはする、とも言えるか。どちらにしてもちょっと面白い。

まとめ

疲れてきたのでこのへんで適当にまとめ。

  • 型指定はPASCAL風というかOcaml風というか基本は変数の後に型を書く。関数の返り値の型は関数シグネチャの直後に書く。
    • 個人的には関数定義とは独立に型を指定できるhaskell風なのが好みなので残念。
  • 関数の仮引数の型は必ず明示する必要がある
  • 返り値の型は省略しても推論してくれる
    • でも再帰になってると推論してくれない
  • 関数定義に"="を忘れるな
  • 無名関数は "=>"で定義可能
  • 変数にはvalとvarの二種類ある。

基本は関数型ながら、手続型っぽい機能もシンプルに取りこんでる感じでわりと好感触。手続スキーな人から見ると真逆の感想になるかもしんない。

あともろもろ気になるところ

局所関数定義
はどうするんだろう。{}でスコープが切れてそうだからその中で定義すればよい?
多相型
Javaジェネリクス的なのが使えるらしい。
代数的データ型
ない?データ構造は全部クラスでやれってことかな。

というわけで(多分)続く。

*1:うお、Scalaシンタックスハイライトされないなあ!

自分をデザイン

元ネタの本は読んでないけど、 自分は決めるものか探すものかって訊かれたら、私は圧倒的に「探すもの」派だなあ。

ただ、「探す」という言葉はちょっと誤解されやすい。「本当の自分」という完成品が どっかに転がってて、それを見つければゴール、みたいな感じにとられるおそれがあるから。

ここでの「探す」は、 木の塊を彫ったり粘土をこねたりして像をつくるとか、そういう感じ。 それは探すじゃなくてつくるじゃないか、と思うかもしれないが、 「もっともふさわしい表現を探す」とか「いちばんしっくりくるデザインを探す」 とかそういう時の「探す」って、「つくる」のとほとんど同義じゃないかな。

(2008/02/16 03:13:23 PST 自分探しんぐ)

この発想はまさに「自分をデザインする」という話で、いかにもschemerらしいなあと思った。

ところで、shiroさんの日記を引用したりブックマークしたりするにはどうすればいいのだろう。パーマネントリンクがなさげで困りはてた。半年くらい立つとこのリンクじゃ辿れなくなるよなあ。