実践Scala入門を読んで

# 背景

就職先の方からオススメされたので、入社前に一通りインプットしてみる。

[実践Scala入門](https://www.amazon.co.jp/%E5%AE%9F%E8%B7%B5Scala%E5%85%A5%E9%96%80-%E7%80%AC%E8%89%AF-%E5%92%8C%E5%BC%98/dp/4297101416)
![実践Scala入門](https://images-na.ssl-images-amazon.com/images/I/41Cqb1JSb6L._SX353_BO1,204,203,200_.jpg)

# 1章 Scalaひとめぐり

## 1-6 はじめてのScalaプログラミング

### 事前にJDK(Amzaon Correto 8)をインストール

インストール方法については省略。

```
$ java -version
openjdk version "1.8.0_292"
OpenJDK Runtime Environment Corretto-8.292.10.1 (build 1.8.0_292-b10)
OpenJDK 64-Bit Server VM Corretto-8.292.10.1 (build 25.292-b10, mixed mode)

$ javac -version
javac 1.8.0_292
```

### REPLでHello World

Homebrewでscalaをインストールする。

```
$ brew install scala
...
==> Summary
🍺  /usr/local/Cellar/scala/2.13.6: 42 files, 23.8MB
```

REPLを立ち上げて、「Hello World!」をプリントする。

```
$ scala
Welcome to Scala 2.13.6 (OpenJDK 64-Bit Server VM, Java 16.0.1).
Type in expressions for evaluation. Or try :help.

scala> println("Hello World!")
Hello World!

```

`:q`でREPLを終了する。

```
scala> :q

$
```

`scala`コマンドにファイル名を渡すと、そのソースコードを解釈して即時実行できる。

```
$ echo 'println("Hello World!")' > hello.scala

$ scala hello.scala
Hello World!
```

### scalacでコンパイルしてみる

コンパイルだけを実行するには、`scalac`コマンドを使う。
だが、先ほど作った`hello.scala`ファイルを`scalac`コマンドに渡してコンパイルすると、エラーになる。

```
$ scalac hello.scala
hello.scala:1: error: expected class or object definition
println("Hello World!")
^
1 error
```

この場合、`scalac`でのコンパイル対象はクラス、オブジェクト、トレイトのいずれかである必要がある。
(これを　コンパイル単位 と呼ぶ)

ここでは、実行可能なmainメソッドが必要なので、mainメソッドを持つobjectを定義するように書き直す。
Appを継承するとmainメソッドの定義を省略できるので、今回はその例を示す。

```
$ echo 'object Hello extends App {
  println("Hello World!")
}' > hello.scala

$ scalac hello.scala
```

ちなみに、Appを継承せずに普通にmainメソッドを定義すると、以下のようになる。

```
object Hello {
  def main(args: Array[(String]): Unit = {
    println("Hello World!")
  }
}
```

`scalac`によって.classという拡張子のついたクラスファイルが作られる。
クラスファイルとは、JVMで実行可能なJavaバイトコードを含むファイルのこと。
以下のように、`scala`コマンドでクラスファイルを実行できる。
なお、`scala`コマンドでクラスファイルを実行する場合、定義されたオブジェクト名やクラス名を指定する(ファイル名ではなく)。

```
$ ls
Hello$.class			hello.scala
Hello$delayedInit$body.class	
Hello.class

$ scala Hello
Hello World!
```

`scala hello.scala`の実行は都度コンパイルしてから実行するので比較的遅いが、事前にコンパイルしたものを実行すると速い。

### 値や変数を定義する

`val`で値を定義するときに`lazy`というキーワードをつけると、その初期化を初回のアクセス時まで遅延できる。

```
scala> lazy val lazyDate = new java.util.Date
lazy val lazyDate: java.util.Date // unevaluated

scala> val date = new java.util.Date
val date: java.util.Date = Mon Jul 12 10:59:07 JST 2021

scala> lazyDate
val res0: java.util.Date = Mon Jul 12 10:59:38 JST 2021

scala> lazyDate
val res1: java.util.Date = Mon Jul 12 10:59:38 JST 2021
```

### Scalaにおける一般的な命名規則

Scalaでは基本的に、大文字によって区切るキャメルケースが好まれる。
また、定義については大文字始まりのキャメルケースで定義する傾向がある。

```
//　パッケージ名は . 区切りで階層を表現する
// Javaと同様domain名を逆にするケースが多いが、必ずしもそうである必要はない
// また、 - は使えないので、代わりに _ を使う
package jp.co.example.something_important

// クラス、オブジェクト、トレイト名は大文字で始めるキャメルケースで定義する
// フィールド名などは小文字始まりのキャメルケースで定義する
class MyClass(val myNumber: String) {

  // インデントは半角スペース2個

  // 定数は大文字始まりのキャメルケースで定義する(Scala公式サイトでの規約)
  val DefaultNumber = 42

  // メソッド名も小文字始まりでキャメルケース
  def printSomething(): Unit = println("something")
}
```

### 関数オブジェクトを作る

Scalaでは厳密にはメソッドと関数は別の意味を持つ。
メソッドは`def`キーワードを使って以下のように定義する。

```
scala> def isAlphanumeric(str: String): Boolean = str.matches("[a-zA-Z0-9\\s]+")
def isAlphanumeric(str: String): Boolean

scala> isAlphanumeric("Amazon EC2")
val res2: Boolean = true

scala> isAlphanumeric("日本語")
val res3: Boolean = false
```

このメソッドから関数オブジェクトを生成できる。
以下のようにメソッド名と _ を続けると、関数オブジェクトとして値が返る。

```
scala> val isAlphanumericF = isAlphanumeric _
val isAlphanumericF: String => Boolean = $Lambda$1097/0x0000000801086538@61d6c8c4
```

関数オブジェクトは、関数リテラルや`Function0`~`Function22`型のオブジェクトを生成することで得られる。
まず、以下は関数リテラルによる生成のコード例。

```
scala> val isAlphanumericF = (str: String) => str.matches("[a-zA-Z0-9\\s]+")
val isAlphanumericF: String => Boolean = $Lambda$1100/0x0000000801087358@f9cd1e6
```

以下は関数リテラルではなく`Function1`インスタンスを直接生成しているコード例。
`Function1`の「1」は関数の引数が1つであることを示す。
中で定義されている`apply`メソッドは、`Function1`型のオブジェクトが持つ唯一のメソッドで、関数オブジェクトが実行されるとき、このメソッドが呼ばれる。
こうしてみると、「関数オブジェクト」と言っても、見慣れた普通のオブジェクトであることがわかる。

```
scala> val isAlphamericF = new Function1[String, Boolean] {
     |   def apply(str: String) = str.matches("[a-zA-Z0-9\\s]+")
     | }
val isAlphamericF: String => Boolean = <function1>
```

このようにして生成した関数オブジェクトは他の関数や引数として渡せすことができる。
以下の例にあるような`Seq[String]`の`filter`メソッドは、`String => Boolean`の関数を引数に取る。
先に定義した`isAlphamericF`をそのまま渡すことができる。

```

scala> val words = Seq("Scala", "2.12")
val words: Seq[String] = List(Scala, 2.12)

scala> val alphamericWords = words.filter(isAlphamericF)
val alphamericWords: Seq[String] = List(Scala)
```

## 1-8 ビルドツールの利用

### sbt

Scalaにはsbtという広く利用されているビルドツールがある。

sbtは基本的にはScalaプログラムのコンパイルおよびパッケージビルドを行うためのツールだが、それだけでなくタスクの実行もできるだので、テストの実行などプログラム開発に必要な作業はすべてsbtでできるようになっている。
また、sbtプラグインを実装すれば、誰でも拡張できるようにもなっている。

sbtはLightbend社に所属するエンジニアがSalaコアチームと連携しながらフルタイムワークで精力的に開発しているツール。
最もScalaのビルドに最適化されたビルドツールと言える。

REPLもsbtでは`console`というコマンドで起動できる。
REPLを起動するためにscalaコマンドを入れている場合は、実はsbtさえあれば`scala`コマンドがインストールされていなくても構わない。
`scala`コマンドと違ってsbtでREPLを起動する場合は、利用するScalaのバージョンを切り替えることも簡単にできる。

以下は`set scalaVersion := {version}`でScalaのバージョンを切り替えて`console`を立ち上げ直しているコマンド例。

```
$ sbt
[info] Updated file /Users/glaciermelt/environment/test_0712/project/build.properties: set sbt.version to 1.5.4
[info] welcome to sbt 1.5.4 (Homebrew Java 16.0.1)
[info] loading project definition from /Users/glaciermelt/environment/test_0712/project
[info] set current project to test_0712 (in build file:/Users/xxx/environment/test_0712/)
[info] sbt server started at local:///Users/xxx/.sbt/1.0/server/54048a0bbc2aa2ba2be2/sock
[info] started sbt server

sbt:test_0712> scalaVersion
[info] 2.12.14

sbt:test_0712> console
https://repo1.maven.org/maven2/org/scala-sbt/compiler-bridge_2.12/1.5.5/compiler-bridge…
  100.0% [##########] 52.2 KiB (34.9 KiB / s)
[info] Non-compiled module 'compiler-bridge_2.12' for Scala 2.12.14. Compiling...
[info]   Compilation completed in 7.626s.
[info] Starting scala interpreter...
Welcome to Scala 2.12.14 (OpenJDK 64-Bit Server VM, Java 16.0.1).
Type in expressions for evaluation. Or try :help.

scala> :q

[success] Total time: 85 s (01:25), completed 2021/07/12 15:39:41
```

このようにsbtなどのScalaのビルドツールとJDKさえあれば、Scalaのプログラムのコンパイルと実行、パッケージングだ毛でなく、REPLの実行まで必要な作業はすべて可能。
`scala/scalac`がインストールされていなくても良いという理由がご理解いただけただろうか？

# 2章 Scalaの基礎

~2.3まで確認済み。
続き：2.4~