kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

『やさしいScala入門 平明な例と演習問題で学ぶ』の感想というかダメだし

発売日は2/22だが、秋葉原の書泉で先行販売?されてたので、購入した。この本の著者がこれまでに書いた本の評判がよろしく無いらしいことは、Twitterで事前に情報を入手していたから知っていたので、内容のひどさについてはある程度覚悟していたが、こりゃダメだ。何がダメって、細かい間違いがいくつもあるとかそれどころではなく、全体的にダメだ。以下、どのようにダメなのかについて、簡単に説明してみる。

関数型プログラミングについてほとんど触れられていない

「はじめに」には、次のようにある:

(snip)
オブジェクト指向関数型プログラミングの両方を利用できるということは、Scalaをマスターするためには両方の概念と用語、そして、それらを結合する新しい概念とその用語を学ぶ必要があるということです。しかし、本書では「Hello,Scala」を出力するための単純なプログラムからはじめて徐々に高度な内容になるので、誰でも気楽にScalaを学ぶことができます。
(snip)

これを見ると、オブジェクト指向関数型プログラミングの両方について本書できちんと触れられているかのような書き方である。しかし、本書では関数型プログラミングとはどのようなものであり、どのような利点・欠点があるか、また、Scalaの機能を使うことで、関数型プログラミングがどのように容易になるかについてほとんど解説がなされていない。「1.1 Scalaとは」において、わずかにp.3において、

(snip)
関数型プログラミングとは、(原則として)副作用を持たない関数を記述することでプログラムを作成する手法です。
(snip)

という形で触れられている程度であって、これで関数型プログラミング(の基本)を学ぶというのははっきり言って無理がある。Scalaは関数型とオブジェクト指向のハイブリッド型であることをウリにしているのだから、関数型プログラミングについてまともに触れられていないのはさすがにあり得ないひどさだ。

Scalaの特徴的な機能があまり紹介されていない

ページ数の制限などもあるだろうし、全ての機能を紹介しろとは言わない。しかし、基本的な制御構文(if,whileなど)や基本的なオブジェクト指向機能(classやobject)については紹介しているのに、case class+パターンマッチ*1、第一級の関数*2ジェネリクス、などの、関数型プログラミングをサポートする機能の多くがまともに紹介されていないのはいかがなものだろうか。コメントがネストできるとか正直入門ではどうでもいいことにいちいちページ割くくらいなら、他に説明すべき事があるだろう。printfの書式指定にわざわざ3ページも割くとか、力入れるべきところを完全に間違っている。

基本的な事実誤認や珍妙な解説が散見される

「インタープリタ言語でもありコンパイラ型言語でもある」?

p.3

インタープリタ言語でもありコンパイラ型言語でもある

Scalaプログラムは、インタープリタ上でも、Java仮想マシン(以下、Java VM)上でも動作します。特に、インタープリタ上では対話型でプログラミングを進めることができるというのは、JavaC#C++にはない機能です。

おそらくREPLの事を指していると思われるが、あれは、実際にはJava仮想マシンのコードにコンパイルしていて、動作方式としてはscalacでコンパイルしたときと本質的な違いは無い。便宜上ScalaのREPLをインタプリタと呼ぶこともあるが、書き方からして、あえて説明上のウソを言っているというより、単に知らないだけな気がする。

ScalaのソースはUTF-8しか使えない?

p.11のNote

日本語を含むScalaプログラムのソースファイルは、UTF-8(BOMなし)で保存してください。(snip)ソースプログラムで英数文字だけを使い、日本語を含めない場合は、シフトJISや他の文字エンコーディングを使える場合があります。

普通に-encodingオプションでUTF-8以外のエンコーディングが使えるのだが?実は、後の方で-encodingオプションの話はちょこっと出てくるのだが、こんなまぎらわしい書き方はやめて欲しいものだ。

スコープが異なるとvalを再初期化できる?

p.47

通常、valで定義した値は再初期化できませんが、スコープが異なると、valで定義した値を再初期化することができます。(snip)

単に内側のスコープで定義したローカル変数によって外側のスコープの変数の定義が隠蔽されるだけなのだが、著者はそのことを知らないのか?

単純な式の場合は、関数の定義を囲む波括弧を省略できる?

p.51

この例のように単純な式の場合は、関数の定義を囲む波括弧({、})を省略しても構いません。

>scala def twice(n: Int): Int = x * 2

何故こういう勘違いをしたのかわからないが、=の右辺は式である必要があって、かつ、{ ... }は式を構成するので、複数の式が必要な定義の場合、... = {}のように書けるだけのことであって、話が全く逆だ。

「名前のある関数リテラル」?

p.57

varを使って名前と共に関数リテラルを定義すると、後から名前でその関数リテラルを繰り返し呼び出すことができます。次の例では、引数の値を乗算して返す関数multを定義して、それを呼び出しています。

scala> var mult = (v1: Int, v2: Int) => v1 * v2
(snip)

単に関数の値を変数に代入しているだけのことなのに、何か特別なことをしているかのように書く意味がわからない。というか、multに後で別の関数を割り当てるわけでもないのに、何故varを使う?

if式のelseの前で改行できない?

p.63

しかし、多くのプログラミング言語の記述方法として一般的なelseの前で改行する次のようなコードは、Scalaではエラーになります。

if(x == 0)
  println( "Zero" )
else
  println( "Other" )

上記のコードは、全く問題無くコンパイルできるのだが?エラーになるとしたら、REPLの場合だが、もしかして、REPLでしかこのコード試して無かったんじゃなかろうな。

Rangeはobject以外にクラス(class)も定義されている?

p.74 COLUMN

なお、Rangeはobject以外にクラス(class)も定義されていて、クラスのオブジェクト(インスタンス)はuntilを使って次のように定義できます。

scala> val rc = 1 until 10
rc: Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)

ここは全く意味不明。一体何がいいたいのだろうか。

ArithmeticExceptionを無意味にcatchしている

例外処理に関する説明のところで、何の断りもなしにArithmeticExceptionをキャッチしているが、ArithmeticExceptionは通常はバグであり、無闇にcatchするべきものではない。あえて例として使用したいなら、その事は断っておくべきだろう。

existsのパラメータはブールの値を返す式?

p.135

existsを使うと、Listの中に特定の条件の要素があるかどうかを調べることができます。条件はブールの値を返す式で指定します。

いや、existのパラメータはA => Booleanな関数だから。scaladocちゃんと読んだのだろうか?それ以前に、試してみた時点でなんかおかしいと気付きそうなものだが。

「_」はあらゆるものに一致するパターンを表す?

(上の部分に続いて)
たとえば、namesの中に"Pamera"を含むものがあるかどうか調べるときは、「(_ == "Pamera")」という式を使うことができます。「_」はあらゆるものと一致するパターンを表します。

いや、全然違うから。「_」があらゆるものと一致するパターンをあらわすのは、パターンマッチのパターンとして出現したとき。これも、書いていて、なんかおかしいな、とは思わなかったのだろうか。

説明無しに無名クラスを使ったコードが登場する

p.157

ウィンドウに「Hello, Scala」という文字列を表示するLabel(ラベル)を配置したいときには、次のようにします。

title = "Hello"
val label = new Label { text = "Hello, Scala" }
contents = label

何の説明も無しに無名クラスを使ったコードをいきなり出すのはどうなんだろう。Javaプログラマが対象だとしても、Scalaでは無名クラスは〜のように書けます、という説明は最低限必要だろう。

説明無しに突然PartialFunctionのリテラルを使ったコードが登場する(p.161)

上記の話とほぼ同様なので、中身については省略。

まとめ

『やさしいScala入門』はひどい本である。

  • 関数型プログラミングについてほとんど触れられていない
  • Scalaの特徴的な機能があまり紹介されていない
  • 基本的な事実誤認や珍妙な解説が散見される

とりあえずこんなところで。

*1:switch文の代替としての使い方しか解説されておらず、パターンマッチの一般形がどのようなものかについて全く説明されていない

*2:一応、無名関数などについては簡単に触れられているものの、ほとんどページ数は割かれていない