Re: Re: 勉強帳 (1)

ref: http://d.hatena.ne.jp/kmizushima/20090513/1242218451
みずしまさんにいろいろ教えてもらってしまいました。ありがとうございます。なんでも晒してみるもんですね。

static メソッドと普通のメソッドが混ざってるクラスは表現できるのかしら。

staticメソッドと普通のメソッドが混ざっているクラスは、companion objectという、classと同じコンパイル単位・同じ名前で定義されたobjectによって表現します(classとobjectは別の名前空間を持ちます)。companion objectはcompanion classに対して特権的なアクセス権を持っており、privateなフィールドにもアクセスできます(companion objectからのアクセスを防ぎたい場合、private[this]とします)。

なるほど。なんか、Java のクラスから static メンバとそうでないメンバを無理やり分けたような仕様ですね。

これもめちゃ遅い!対話的環境で試すしかないか。

起動時間とコンパイルの遅さはいかんともしがたいところですね。この辺はあきらめるしか。コンパイル時間については、Scalaディストリビューションに付属のfscという、コンパイラのデーモンが常駐するプログラムを使えば多少は緩和できます。

fsc を試してみました。scalac だと 4.8 秒くらいのが、fsc だと 1.6 秒くらいになりました。うーん、早いけど、それでもやっぱり遅いですね。あきらめます。

文法の話があった。一引数のメソッドはなんでも中置にできるらしい。

厳密に言うと、レシーバを指定した呼び出しでは、任意のメソッドについて中置にできて(.を省略できて)、一引数の場合、さらに引数を囲む括弧も省略でる、という形になります。たとえば、以下のような呼び出しが可能です。

おお、なるほど! こういう面白いとこはチュートリアルでもちゃんと書いてほしいですね。

ふんふん。メソッド名を括弧なしで書けば関数オブジェクトが取り出せる。Python 風な

これは少し違いまして、メソッドから関数オブジェクトを取り出すには、一般には、メソッド名 _とする必要があります。上の例だと、foo(bar _)となります。

なるほど。bar _ は x => bar x の syntax sugar みたいなもんですね。

ただ、関数オブジェクトを期待している箇所に対して、それに適合する型のメソッドが現れた場合、暗黙に関数オブジェクトに変換してくれるというようなルールがあるため、上の場合、メソッド名を括弧無しで書くだけで関数オブジェクトに変換してくれます。

うひゃ、なかなかアドホックなルールですね。これは implicit conversion とかいう枠組みとは別っぽい?

うーん、{ println("foo") } は無引数の匿名関数なんですかね。

これは、{ println("foo") }が特別なのではなくて、def foo(f: => Unit)の中の、f: => Unitの部分で、引数fの評価が遅延される(call-by-name)ことを指定しているのがミソです。

callee 側で遅延評価を指示できるのは創意工夫のやりがいがありそうでとても面白いですね。{ ... } は Ruby の begin ... end みたいなものだと思うことにします。
あと、foo { println("foo") } で括弧が省略できてしまってるのにも、さりげなく混乱してます。

コンストラクタをオーバーロードで複数用意する方法はないのかな。

def this(引数1, ...)のようにすることで、コンストラクタをオーバーロードすることができます。

いやー、これはなかなかかっこ悪いですね (ぉ

歴史的事情もあって、バッドノウハウ的な話になるのですが、実は、「0引数のメソッド」も問題無く括弧を省略することができます。あと、「0 引数のメソッド」も「無引数のメソッド」のどちらも、メソッド名 _で関数オブジェクトを得ることができます。この辺の仕様のごちゃごちゃした部分は、将来のバージョンで修正されるとかされないとか。

なるほどー。メソッド呼び出しの括弧がやたらと省略できる Ruby と全然省略できない Python の一長一短を両取りする設計の登場か、と期待したんですが、なんかいろいろあるんですね。

パターンマッチに関してですが、matchというキーワードを使う形が本来の形で、 { case ... }だけの式は、exp => exp match { case ... }という無名関数のシンタックスシュガーになります。型推論については、関数オブジェクトを要求している事がわかっている文脈であれば、適切に推論することができます。たとえば、以下のように書けます。この辺、MLやHaskellに比べて、型推論が機能する場所がある程度限定されているわけですが、Scalaの型システムを考えると、やむを得ないかなあ…と思います。

うーん、Scala の型システムを考えられるようになってからまた考えます。

ちなみに、matchが中置形式になってることですが、別に文法上特別な意味は無いです。

なるほど。{ case ... } の方がプリミティブだと思ったんですが、こっちが syntax sugar に過ぎないんであれば、中置に意味はなさそうですね。{ case ... } をプリミティブにすればよかったのに。

この辺を静的型付けでチェックしてくれるのは結構うれしそう。本当にチェックしてくれるのか知らないけど。もちろんしてくれるよね?

はい。もちろん、普通に型チェックされます。

よかったです。ありがとうございました。