Scalaことはじめ その1

# はじめに

Scalaを勉強しようと思い立ったので、あとで自分で見直せるように調べたことを書いていきます.
この記事では、環境構築から`Hello World`を動かすまでが対象です.
おかしな点があれば、コメントにてご指摘いただけますと幸いです。

## Scalaとは？

JavaVM上で動く、オブジェクト指向言語と関数型言語のマルチパラダイムな言語.
Javaのライブラリを使用でき、かつJavaにはない機能や効率的な書き方がいろいろある「らしい」です.
よく `Better Java` として語られていることがありますが、それをScala界隈の人にいうと~~マサカリが飛んできます（主観）~~.

## Scala環境について

### 関連パッケージ

- Java(JDK)
    - Scalaは、JavaVM上で動作するため、インストール必須です.
- sbt
    - Scalaのビルドツール (JavaのAnt, Maven的なもの？）で、JavaのMavenで配布されているパッケージを再利用できます.
    - Java8と、Java9以降で対応しているScala(sbt)に制限があります.
        - 参考) https://xuwei-k.hatenablog.com/entry/2018/01/20/203548
- Play Framework(Play)
    - ScalaとJavaに対応したのWeb Application Framework.
- Typesafe Activator(EOL)
    - Play2.4.xまでPlay Frameworの実行には `activator`コマンドを使用していたようですが、すでにEOLとなっており、Play2.5以降は `sbt` コマンドを使用するようです.
    - (余談)Scala環境構築で調べると、 `Typesafe Activator` をインストールする手順になっていて、latest versionが見つけられずとても迷いました.

とりあえず、 `java` と `sbt` が入っていればなんとかなりそうな感じです.
他にもDB回りのパッケージがあるらしいですが、まずは動かすとこまでなので割愛します.

### 環境とバージョン

以下が入ったDocker imageを作ります.
Versionはすべて、執筆時のlatset versionです.

- jdk 11.0.4
    - Scala(sbt)のlatest Versionを使っていれば、Java11でも動きそうなのでJava11を使います.
- sbt 1.2.8
    - version listはこちらから確認しました https://www.scala-sbt.org/download.html
- Scala 2.13.0
 
### Dockerfile

自分でDockerfileを書くのが面倒だったら、こちらのjava+sbtのイメージ使っても良いです.
https://hub.docker.com/r/hseeberger/scala-sbt/

特にバージョンを気にしないのであれば、↓がシンプルです.

```dockerfile
FROM openjdk:11.0.4-jdk-stretch

ENV LC_ALL C.UTF-8
ENV LANG C.UTF-8

# Add repository for sbt
RUN echo "deb https://dl.bintray.com/sbt/debian /" | tee -a /etc/apt/sources.list.d/sbt.list 

# Update packages
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv &&
    apt-get update &&
    apt-get install -y --no-install-recommends apt-transport-https gnupg ca-certificates vim software-properties-common &&
    apt-get install -y sbt

# Install Scala
RUN \
  curl -fsL https://downloads.typesafe.com/scala/$SCALA_VERSION/scala-$SCALA_VERSION.tgz | tar xfz - -C /root/ && \
  echo "export PATH=~/scala-$SCALA_VERSION/bin:$PATH" >> /root/.bashrc

COPY . /root/work
WORKDIR /root/work

RUN sbt about
```

sbtやscalaのバージョンを指定するため、 https://hub.docker.com/r/hseeberger/scala-sbt/ を参考に少しスリムにしたものを用意しました.

```dockerfile
FROM openjdk:11.0.4-jdk-stretch

ENV LC_ALL C.UTF-8
ENV LANG C.UTF-8

# Versions
ARG SCALA_VERSION
ARG SBT_VERSION

# Envs
ENV SCALA_VERSION ${SCALA_VERSION:-2.13.0}
ENV SBT_VERSION ${SBT_VERSION:-1.2.8}

# Update and install packages 
RUN apt-get update && \
    apt-get upgrade -y && \
    apt-get install -y --no-install-recommends apt-transport-https gnupg ca-certificates vim 

# Install sbt
RUN curl -L -o sbt-$SBT_VERSION.deb https://dl.bintray.com/sbt/debian/sbt-$SBT_VERSION.deb && \
    dpkg -i sbt-$SBT_VERSION.deb && \
    rm sbt-$SBT_VERSION.deb && \
    apt-get update && \
    apt-get install sbt 

# Install Scala
RUN \
  curl -fsL https://downloads.typesafe.com/scala/$SCALA_VERSION/scala-$SCALA_VERSION.tgz | tar xfz - -C /root/ && \
  echo "export PATH=~/scala-$SCALA_VERSION/bin:$PATH" >> /root/.bashrc

COPY . /root/work
WORKDIR /root/work

# Initialize
RUN sbt about
```

#### ポイントだけ説明

「sbt install」で検索すると、`apt-get`でinstallする手順が見つかります.
https://www.scala-sbt.org/1.x/docs/ja/Installing-sbt-on-Linux.html
この手順をDocker上で実行すると、パッケージ不足によりエラーが出るため、1つめのDockerfileではいくつかパッケージを追加してます.

```dockerfile
# Update and install packages 
RUN apt-get update && \
    apt-get upgrade -y && \
    apt-get install -y --no-install-recommends apt-transport-https gnupg ca-certificates vim
```

- 補足
    - sudoはroot userで実行しているので除いています
    - 最後のvimはおまけです


sbtのバージョンを指定したい場合は、debから直接読み込んでからnstallします.
2つ目のDockerfileはこの手順です.

```dockerfile
# Install sbt
RUN curl -L -o sbt-$SBT_VERSION.deb https://dl.bintray.com/sbt/debian/sbt-$SBT_VERSION.deb && \
    dpkg -i sbt-$SBT_VERSION.deb && \
    rm sbt-$SBT_VERSION.deb && \
    apt-get update && \
    apt-get install sbt 
```


##### キャッシュ

`sbt` を実行すると初回だけ色々とリソースをダウンロードしてくるため時間がかかります.
version表示するだけの `sbt about` コマンドを実行して、docker build時にリソースを予め取得しています.

```dockerfile
RUN sbt about
```

### Hello world

#### docker build and run

以下のコマンドでDocker containerに入ります.

```bash
dokcer build -t jdk-sbt .
docker run -it jdk-sbt /bin/bash
root@2644fdca871c:~/work# 
```

#### Scala コマンドの場合

適当に `HelloWorld.scala` を作成します.

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

そのまま、 `scala` コマンドに投げると結果が返ってきます.

```bash
root@2644fdca871c:~/work# scala HelloWorld.scala 
Hello World
```

javaと同じように、コンパイルしてから実行もできます.

```bash
root@2644fdca871c:~/work# scalac HelloWorld.scala 
root@2644fdca871c:~/work# ls
Dockerfile  HelloWorld$.class  HelloWorld.class  HelloWorld.scala
root@38cf45ac5726:~/work# scala -cp . HelloWorld
Hello World
```

#### sbt コマンドの場合

以下のコマンドで、scalaのprojectの初期化ができます.
`scala/scala-seed.g8` は最小構成のscala projectのtemplateのパスです.
途中でプロジェクト名を聞かれますが、プロジェクト名には大文字の英字は使えません(エラーは出ませんが、勝手に小文字に変換されます）.

```bash
root@2644fdca871c:~/work# sbt new scala/scala-seed.g8
[info] Set current project to work (in build file:/root/work/)
[info] Set current project to work (in build file:/root/work/)
A minimal Scala project. 

name [Scala Seed Project]: HelloWorld
Template applied in /root/work/./helloworld
```

プロジェクトの構成を見ると以下のようになっています.
Javaと似たような構成で、src配下にcodeを配置していきます.

```bash
.
├── build.sbt
├── project
│   ├── Dependencies.scala
│   └── build.properties
└── src
    ├── main
    │   └── scala
    │       └── example
    │           └── Hello.scala
    └── test
        └── scala
            └── example
                └── HelloSpec.scala
```

このプロジェクトをbuild&runすると、 `Hello.scala` のcodeが実行されます.

```scala:Hello.scala
package example

object Hello extends Greeting with App {
  println(greeting)
}

trait Greeting {
  lazy val greeting: String = "hello"
}
```

`helloworld` ディレクトリに入り、 `sbt run` で実行できます.
最初のみごちゃごちゃと色々出力がありますが、 最後に `hello` が表示されればOKです.

```bash
root@2644fdca871c:~/work# cd helloworld
root@2644fdca871c:~/work/helloworld# sbt run
:
[info] Running example.Hello 
hello
[success] Total time: 17 s, completed Jul 29, 2019, 2:14:46 PM
```

`Scala` と `sbt` でそれぞれ実行できました.


### コミュニティ

困ったらここで質問すると良さそうです.
https://gitter.im/scalajp/public

### 参考にさせて頂いた記事

https://qiita.com/u6k/items/bbf827de62fc8905d98f