自己紹介とトークテーマの背景

中村学氏(以下、中村):まず自己紹介します。中村学と言います。「がくぞ」という名前でTwitterなどをブイブイやっているので、よかったらぜひフォローしてください。Tech to Valueという会社の代表と、あとはエフ・コードでCTOをやっています。

さっそく中身に入ります。Scalaは学習コストが高いと言われることがあります。多言語からの経験から、けっこう推測しづらい言語機構があったりするので、この辺がけっこうとっつきにくいと感じる方がいるようです。

他の言語では専用で用意されている構文のようなものが、Scalaだとシンプルな機能を組み合わせることで実現されています。組み合わせで実現できることがすごく増えるので、あたかも機能がいっぱいあるかのように見えてしまい、とっつきづらく感じることがあったりします。

そのへんがあるので、このセッションでは、最初に知っておくとよさそうな構文と、言語機構について説明したいと思っています。

ブロック式とは?そもそも式とは?

まずはブロック式について話をします。ブロック式って、みなさんご存じですかね? ブロック式の説明の前に、そもそも式とは何でしょうか。式そのものの形式的な定義は言語仕様に書いてあります。しかし、それを見るとちょっとわかりづらかったので、ザックリ語弊もあるかと思いつつ言うと、Scalaにおいて式は“プログラムの実行における一番基本的な要素”です。

どういうことかと言うと、その文字列リテラルのfooとか、あるいは1+2+3のような、普通のメソッド呼び出しのようなものも全部式です。最近の言語にはよくあるんですが、 if も式になります。

式とは何かを一番端的に表しているのが、他の言語だとよく“実行”とも言われる“評価”で、そのプログラムの中の書かれている部分を評価して、成功すると値になるものが式です。Stringや文字列リテラルであれば、それがStringのインスタンスになりますよね。あるいはその1+2+3のようなメソッドの呼び出しがあると、普通に計算されて、評価が成功すると6になり、これが、いわゆる式と言われるものになります。

評価しても値にならないものは“文”

式じゃないもの、評価をしても値にならないものを文と言います。ステートメントと言われるものです。具体的に何かというと、変数宣言などのようなもので、val bar = "the value declaration is a statement"のような変数宣言は、評価しても変数が定義されますが、それ自体が何か結果を返すわけではありません。

例えば、throw new Exceptionのようなものも、実は式です。throwもthrow式ですが、throwは例外を投げるため、評価が必ず失敗します。評価しても値にならず、例外になる式です。

メソッド定義について

メソッド定義について見ていきたいと思います。メソッド定義そのもの自体はステートメント(文)ですが、メソッド定義の成り立ちは、defから始まってメソッド名が来て、その後ろに( )で引数リストが来ます。これが、引数の名前と型のリストです。その後ろにコロンで戻り値の型が来て、そのあとにイコールがあって、その右側に式が来ます。

メソッド定義は、基本的にはdefから始まり、大きくイコールで区切られる感じです。イコールの左側が、メソッド名や引数リスト、戻り値など、メソッドのシグネチャになるところ。イコールの右側に単一式を取ります。式の評価結果がメソッドの結果、戻り値になるような構造になっています。

完全なメソッドの定義のコードが、いわゆるここで足し算をするようなメソッドの定義です。def add(a: Int,b: Int)戻り値の型がIntで、右辺にa + bという式を書いている。これが完全なメソッドの定義ですが、ここにreturnも{ }もないことに注目してもらえると一番いいと思います。これはもう完全に、完璧なメソッド定義のかたちです。

時折りScalaのメソッド定義ではreturnが省略できるとか、{ }を省略できるという表現をすることがありますが、厳密にはちょっと正しくなくて。あくまで上記のイコールの右側に単一の式が来るのが一般的なかたちで、必要であればreturnや{ }も書いてもかまわないのが実際のところです。このニュアンスの違いわかりますか?(笑)。

ここが大事なところというか、僕が一番伝えたかったので。returnや{ }は省略ではないよ、というところです。

複数の式や文を1つの式としてみなす“ブロック式”

式はそのもの自体が評価されると値になるので、それ自体をまた式の一部として、大きな式を作れます。そのため、複雑な処理自体を単一式で表現することも、基本的には可能です。ただ、何回も同じ値を使いたいときや、何回も評価したい場合、あるいは処理の意図を宣言的に表現したい場合は、単一式であるより、式を分けたほうがわかりやすくなる場合がけっこうあります。

そこで、ブロック式が導入されます。ブロック式は複数の式や文をまとめて、1つの式としてみなすためのものです。ブロック式が評価されるときには、上から順番にその式が評価されて、最後の式の値、評価結果がブロック式全体の評価結果になる構造です。

さっき書いたような単一式でいろいろ呼び出しているような式も、calculate(foo)が2ヶ所に出てきていますが、ブロック式を使ってbazという名前を付けたり、calculate(bar)にquxと名前を付けたところに、最後のdoSomethingの呼び出しを式として書くことで、同じ意味を表せる感じです。

ブロック式を使うと、上のコードと下のコードが同じ意味合いになります。あくまでブロック式の最後の式自体が全体の評価結果になるので、そもそもreturnが省略されているわけではなく、単純にそういう構造ですよ、という感じです。ブロック式自体は、あくまでその式を束ねる、ただの式なので、メソッドとか関係なく、式が書ける場所だったらどこにでも置けます。

そのため、メソッド定義以外に、普通に変数定義のfooのところでブロック式を使ってもかまいません。これは普通にprintlnして、10+30の2つの式を組み合わせて、1つの式になっている。結果、定義時にprintlnされて、値としては40が代入されるかたちになります。

同じように、if式の条件式が合致するケース、あるいは条件が合致しないケースにも、単一の式が書けます。そのため、ifで{ }があるときとないときは構文上の違いがなく、あくまで条件の後ろ、elseの後ろにはには1つの式が来ますよと。

その式の中で複数のことをしたいのであれば、ブロック式を使えば単一の式としてみなされるので、他の言語にあるような、{ }付きの書き方も許容されます。

そもそもreturnとは何か?

では、そもそもreturnとは何かというと、あくまでそのメソッドの中で、その評価の途中で脱出して値を決定したいときのための構文です。そのため、メソッド定義の外では使えません。変数定義のbarやフィールドを定義するときにブロック式を使ってreturn bazを書いても、そもそもメソッドの定義ではないので、コンパイルが通りません。

あくまでreturnを使いたいときは、メソッド定義の中でガード節などを作る一番先頭で、条件に合わなかったらこのメソッドの評価結果を決定したい、後ろの評価を打ち切って返したい、というような特殊なときにreturnを使います。それ以外のときはブロック式が全体の評価結果になるので、returnを使う必要はありません。

他の言語から移植して書くと、{ return a+b }のように書くことも可能で、これ自体を別に定義したところで、結果自体は同じになります。そのため、だいたい見逃されているというか、書けますが、あたかも他言語のforループやループ文の最後で、わざわざ明示的にcontinueを書いているようななことと同じ感覚です。

本来必要ないのに、あえて途中でreturnを入れているような感じなので、繰り返すとreturnは省略できるわけではなく、単純にその式の評価結果自体がメソッドの結果になるだけです。省略できると言うと、書いていなくても実際のプログラムの意味上ではreturnが補足されて扱われるような意味合いになってしまいますが、そうではなく、returnがないかたちが通常です。

ブロック式の要点まとめ

まとめると、式は評価すると値になるプログラムの構成部品です。ブロック式は複数の式や文をまとめて1つの式にします。ブロック式は普通の式なので、式が置けるところにはどこでも置けます。returnやブロック式の{ }は省略できるわけではなく、必要であれば使用する感じです。

けっこう他の言語だと、メソッドの定義のときに、{ }があるパターンとないパターンが両方の言語仕様として複数定義されている。複雑にルールが定義されている場合があります。しかし、Scalaの場合、メソッドの定義としてはイコールの右辺に式が置ける、1つのシンプルなルールで成り立っています。その式の中にはブロック式という存在がある感じです。

メソッド定義のときに、{ }があるときと{ }がないときの両方を許容する言語に比べると、シンプルなルールで成り立っています。ここまでがブロック式の説明でした。

(次回につづく)