吉祥寺.pmのブログに掲載していた「Scala関数型デザイン&プログラミング」のexerciseを解く時の環境構築について、少し修正してこちらに載せ直しました。イベントブログにだけ載っているのも、もったいないな、と思って再掲。
環境構築
JDK(Java Development Kit)のインストール
Scalaはご存じの通り、JVM(Java Virtual Machine)上で実行される言語です。そのため、Scalaを使うためにはJDK(Java Development Kit)をインストールする必要が有ります。
Oracleのサイトからダウンロード
JDKのインストール方法はいろいろな所で解説されていますので、ここでは詳細は割愛しますが、自分の環境に合わせてOracleのサイトからインストーラをダウンロードしてインストールしてください。
Homebrewによるインストール
macOSではbrew cask
コマンドでインストールできます。以下の1行で最新版がインストールされます。
$ brew cask install java
OpenJDKのインストール
Linuxの場合は、パッケージマネージャーから簡単にインストールできるOpenJDKを使った方が良いでしょう。
yum
や、apt
といったパッケージマネージャーからインストールしてください。
Gitのインストール
「Scala関数型デザイン&プログラミング」に記載されているコード例や、例題(exercise)のヒント、解答などはすべてGitHub上のリポジトリに置かれています。そのため、サンプルコードをダウンロードするためには、Gitをインストールする必要があります。
Gitのインストール方法もいろいろな所で解説されているので割愛しますが、公式サイトのドキュメントに詳しく書かれていますので、そちらを参考にしてみてください。
macOSではbrew
コマンドでインストールします。
$ brew install git
サンプルコードのダウンロード
任意のディレクトリに、サンプルコードのリポジトリをクローンします。
$ git clone https://github.com/fpinscala/fpinscala.git
これで準備完了です。
特にScalaのコンパイラをインストールしていませんが、そのあたりの仕組みは次の「sbtの実行」で解説します。
コードの実行準備
sbtの実行
ダウンロードしたサンプルコードのディレクトリに、sbt
(Windows用はsbt.cmd
)というコマンドが含まれていますので、まずはこれを実行します。
$ cd fpinscala $ ./sbt
Windowsの場合は、sbt.cmd
を起動します。
$ cd fpinscala $ .\sbt.cmd
sbt
は、Simple Build Toolという身も蓋もないくらい普通の名前が付けられたScala用のビルドツールです。
sbt Reference Manual — 始める sbt
sbt自体がScalaのコンパイラ一式をダウンロードしてくれるので、Scala自体を手動でインストールする必要はありません。
sbt
を起動して特にエラーが表示されなければ、準備はOKです。
sbtはひじょうに高機能なビルドツールですが、のちほど説明するproject
、compile
、console
、run
、test
の5つのコマンドを覚えれば、以降のexerciseのコードを実装する上では十分です。
- project
sbtでは一つのリポジトリで複数のプロジェクトを管理できます。そのプロジェクトを切り替えるためのコマンドです。どのようなプロジェクトが含まれているかは、projects
コマンドで確認します。
- compile
ターゲットのプロジェクトに含まれるすべてのソースコードをコンパイルします。
console
sbt
からScalaのREPL(Read - Eval - Print - Loop)を起動します。run
ScalaもJavaと同様にプログラムの実行は、main関数
から始まります。run
は、main関数
を実行します。
もし、プロジェクト内に複数のmain関数
が含まれる場合は、どのパッケージに属するmain関数
を実行するか、選択するためのリストが表示されます。
- test
Javaや、Scalaでは、テストコードはsrc/test/*
ディレクトリに格納されますが、test
は、testディレクトリ
に格納されたテストコードを実行します。
PermSizeの削除
macOSや、Linux環境で実行する際には、sbt
コマンドを使いますが、Java8以降では不要なパラメータ(PermSize)が書かれており、実行のたびに警告メッセージが出てしまうので、Java8以降のJDKをインストールしている場合は、下記の通り書き換えることをお勧めします。
sbtは単なるシェルスクリプトで、実態はsbt-launch.jarにパラメータを与えて起動しているだけです(Windows用のsbt.cmd
には最初から記述は有りません)。
変更前
SBT_OPTS="-Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256M"
変更後
SBT_OPTS="-Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled"
brachの作成
以降、コードの実装を始めますが、サンプルコードのリポジトリは今でも日々コミットが続いています。自分が書いたコードを区別するためにも、gitのbranchを作成しておきましょう。
$ git checkout -b myexecirse
コードの実装
サンプルコードのディレクトリ構成は大きく分けて、以下の3つに分かれています。
exerciseと、answerが対になっているので、テキストエディタを上下分けて、それぞれ表示しながら進めるのがおすすめです。
exercise
書籍に掲載されているサンプルコードと、exerciseで書かれている関数のひな形(関数名と、引数、返値だけが書かれている)が掲載されています。例えば最初のリストの実装であれば、以下のファイルを書き換えていく形になります。
関数のひな形が掲載されているものはコードの本体が「sys.error("todo")」となっていて、実行するとエラーになるようになっています。まずはこの「sys.error("todo")」という部分を削除して実装を始めることになります。
def tail[A](l: List[A]): List[A] = sys.error("todo")
↓
def tail[A](l: List[A]): List[A] = l match { ... }
なぜか関数のひな形が書かれていないものや、本誌に書かれたひな形と引数名が違ったりするものも有りますが、後述のanswerに書かれている回答の関数名と、引数、返値を見ながら進めると良いでしょう。
answer
exerciseの回答と、その解説が書かれています。
先ほどのListの回答は、以下のファイルに書かれています。
answerkey
exerciseの回答がファイル別に置かれています。また、回答ごとに、ヒントも置かれているので、まずはこのヒントを見ながら実装していくと良いでしょう。
コンパイルと、実行
exerciseのディレクトリ配下に置かれているファイルを書き換えながら進めていくので、exerciseのディレクトリだけがコンパイルされるようにsbt
上のプロジェクトを切り替えておきます。
また、compileコマンド
でエラーが無いことを確認したら、consoleコマンド
でScalaのREPL(Read-eval-print loop)が起動するので、そのまま実装したパッケージ(Listでいえば、fpinscala.datastructures)をロードすることで、実装の確認ができます。
$ ./sbt > project exercise > compile > console scala> import fpinscala.datastructures._ scala> val x = List.Cons(1, Cons(2, Nil)) ...
エラーが出たり、挙動が正しく無いときは、実装の正しさを、answerか、answerkeyを参照して確認します。
あとはひたすら繰り返しです。頑張りましょう。
runコマンド
なお、例題によってはmain関数
を起動するものも有ります(第2章のgettingstarted
など)。それらのmain関数
を起動するときはsbt
からrunコマンド
を実行します。
プロジェクトの中には、複数のmain関数
が有るので、どれを起動するか選択するリストが表示されます。パッケージ名を確認して、該当する番号を入力して、エンターキーを押下してください。
> run ... Multiple main classes detected, select one to run: [1] fpinscala.streamingio.ProcessTest [2] fpinscala.gettingstarted.MyModule [3] fpinscala.gettingstarted.FormatAbsAndFactorial [4] fpinscala.gettingstarted.TestFib [5] fpinscala.gettingstarted.AnonymousFunctions [6] fpinscala.iomonad.IO2aTests [7] fpinscala.iomonad.IO2bTests Enter number:
実装の記録(tips)
exerciseのファイルには、関数のひな形は書かれていても、対象のexerciseの番号が書かれていません。あとで振り返ったときに分かりづらいので、コメントでexerciseの番号を書いておくと便利です(「// exercise 3.3
」のような形式で書いておきます)。
また、いろいろと自分で気がついたところも都度コメントで残しておくと、あとで振り返ったときに便利です。
章ごとに実装が終わったら、gitでコミットしておくとよいでしょう。
また、急激に難易度が上がるところや、最初から「難問」と書かれているような例題も有りますが、そうゆうときはさっさとanswerのコードを写経して、挙動や実装の背景を理解するようにシフトした方が良いでしょう。そのときに気がついたことをひたすらコメントで残しておく方が理解が早いでしょう。
テストコードの追加
sbt
のconsole
を使って実行結果を確認しても良いですが、いまどき実行結果はテストコードを書いて、テスト結果で確認すべきです。
以下に、exerciseで書いたコードをテストコードで確認する方法を紹介します。
Scalaのテスティングフレームワークとしては、ScalaTestか、Specs2がよく使われていますが、ここではScalaTestを使うことにします。
依存ライブラリの追加
Build.scala
のoptsに、ScalaTestへの依存を追加します。
resolversの最後にカンマを追加するのを忘れないように。
val opts = Project.defaultSettings ++ Seq( scalaVersion := "2.11.7", resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/", libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.0" % "test" )
テストコードの追加
exercises/src/test/scala/fpinscala/datastructures/ListTest.scala
というテストコードのファイルを用意します。パスの3番目がtest
になっていることに注意して下さい。Javaではおなじみですが、テストコードはtestディレクトリ
に保存します。
例えば、3章で出てくるtailメソッド
のテストコードは以下のように書けます。
ScalaTestではいくつかのテストスタイルが使えますが、ここでは一番シンプルに書けるFunSuiteを使っています。
最後のshould equal
で期待する値との比較をしています。
import org.scalatest._ import fpinscala.datastructures._ class ListSuite extends FunSuite with Matchers { test("tailメソッドは先頭の要素を削除する") { val listInt = List(1, 2, 3) val listDouble = List(1.0, 2.0, 3.0) val listString = List("one", "two", "three") List.tail(listInt) should equal (List(2, 3)) List.tail(listDouble) should equal (List(2.0, 3.0)) List.tail(listString) should equal (List("two", "three")) } }
テストはsbtから実行します。
$ ./sbt > test ... [info] ListSuite: [info] - tailメソッドは先頭の要素を削除する ... [success]...
最後に[success]が出力されればテスト全体が成功したことになります。テストが一つでも失敗すると[error]が表示されます。
どうように3章に出てくるsetHeadメソッド
のテストを追加します。
import org.scalatest._ import fpinscala.datastructures._ class ListSuite extends FunSuite with Matchers { val listInt = List(1, 2, 3) val listDouble = List(1.0, 2.0, 3.0) val listString = List("one", "two", "three") test("tailメソッドは先頭の要素を削除する") { List.tail(listInt) should equal (List(2, 3)) List.tail(listDouble) should equal (List(2.0, 3.0)) List.tail(listString) should equal (List("two", "three")) } test("setHeadメソッドは先頭の要素を置き換える") { List.setHead(listInt, 4) should equal (List(4, 2, 3)) List.setHead(listDouble, 4) should equal (List(4.0, 2.0, 3.0)) List.setHead(listString, "four") should equal (List("four", "two", "three")) } }
Chapter Note
すべて英語で書かれていますが、本書に載っていないChapter NotesがGitHubのWikiに有りますので、時間に余裕があればこちらも読んでおくと参考になります。
Home · fpinscala/fpinscala Wiki · GitHub
おわりに
「Scala関数型デザイン&プログラミング」は非常に噛み応えが有るというか、じっくり取り組む必要が有るし、exerciseのコードを書いてみても、書いたことの意味をきちんと解説してくれる人が近くにいないと、挫折し易いというか、本当にストロングスタイルな本ですが、最後まで進めると確実に実力がつく良本ですね。
Scala関数型デザイン&プログラミング―Scalazコントリビューターによる関数型徹底ガイド
- 作者: Paul Chiusano,Rúnar Bjarnason,株式会社クイープ
- 出版社/メーカー: インプレス
- 発売日: 2015/04/30
- メディア: Kindle版
- この商品を含むブログ (3件) を見る
「Scala関数型デザイン&プログラミング」はScalaの入門書ではないので、Scalaの入門書としては元祖Scalaの解説本である「Scalaスケーラブルプログラミング」の方がおすすめです。つい先日最新の第3版が邦訳されました。
- 作者: Martin Odersky,Lex Spoon,Bill Venners,羽生田栄一,水島宏太,長尾高弘
- 出版社/メーカー: インプレス
- 発売日: 2016/09/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
なお、技術書と言えば定番のO'Reillyからも何冊かScala本がリリースされていますが、邦訳がなかなかリリースされないですね。
Programming Scala: Scalability = Functional Programming + Objects
- 作者: Dean Wampler,Alex Payne
- 出版社/メーカー: O'Reilly Media
- 発売日: 2014/12/03
- メディア: Kindle版
- この商品を含むブログを見る
Learning Scala: Practical Functional Programming for the JVM
- 作者: Jason Swartz
- 出版社/メーカー: O'Reilly Media
- 発売日: 2014/12/11
- メディア: Kindle版
- この商品を含むブログを見る
Scala Cookbook: Recipes for Object-Oriented and Functional Programming
- 作者: Alvin Alexander
- 出版社/メーカー: O'Reilly Media
- 発売日: 2013/08/23
- メディア: ペーパーバック
- この商品を含むブログを見る