All You Need Is

Scala 再入門 (環境構築)

2024/11/17
プログラミングScalaEmacs

はじめに

最近新しいことを学んでいなかったので Scala 3 を学んでみようと思う。 Scala という言語は昔は少しばかり流行っていたのだが、最近ではめっきり話を聞かなくなってしまった。単に自分が Scala を使わなくなってしまったのが原因の可能性があるが、昔ほどの勢いは無くなってしまっているように感じる。

Scala 2 を Scala 3 が出る前に少しだけ触っていたので、Scala が流行らない理由はわかる。大体思っていることは以下の記事に書かれているが、Scala を触ろうとする人にとっては当たり前すぎで抜けている視点がいくかあるように感じる。

Scala の難しさをインターネットから集めてみる - Lambda カクテル

私も Scala 自体は好きで C 言語はメモリ管理面倒だし、Java は本質的でない部分の記述が多いし、Kotlin は今でこそサーバサイドでも使われているが、まだ発表されて間もない頃で Android アプリ開発専用言語のような立ち位置で汎用的な目的には使いにくいなぁ、と感じていた頃に関数型プログラミングという当時の私にとって未知のパラダイムは惹かれるものがあった。どういうわけか、関数型プログラミングというパラダイムを当時の私は素直に受け入れることができたが現代においても取っ付き難さは健在だろう。

上述に一点だけ追加で流行らない理由を上げるとすると、コーディングの根底にある immutability (不変性) だ。

え?immutable (不変な) なコードを書いて保守性を上げるのは常識でしょ?
と思ったあなたはモダンな言語や関数型プログラミングに頭をやられてしまっている。現代においてもプログラミング初心者が学ぶ言語は mutable (可変な) データを扱うことが前提だ。最近流行りの Python を始め、Java、JavaScript、C、Go、PHP、Ruby、etc. は値は可変であることを前提にコードが書かれることが多い。もちろん、これらの言語で再代入のないコードを書けないというわけではない。これらの言語は変数が可変であることを良しとしているため、入門サイト、入門書、世の中に存在しているコードは変数の値が変更されることが当然のように行なわれている。

しかし、Scala はどうだろうか?

そのようなコードに慣れ親しんだ人が Scala を触るとどうなるか。 Scala でも var を使えば再代入はできるけど多くの場面では val が使われる。つまり、再代入をしないようなコードの書き方をすることが前提となっている。そういったコードを読み慣れていない、書き慣れていない人にとってはこれも最初の障壁となると思う。

昔とは違い ES6 が浸透した現代においては const を使って再代入をある程度控えるコーディングが普及しているので、初学者の人も再代入をしないことがデフォルトになりつつあるかもしれない。そういう意味では、不変性については私が思っているほど障害にはなっていない可能性はある。

閑話休題。

さっそく Scala 3 を学ぶために環境構築する。

環境構築

昔は sbt だけ入れればよかったが現在では Coursier というツールを使うのが一般的なようだ。インストール後に cs list を実行するとわかるが、これらのコマンド管理が行なえるらしい。

1
$ cs list
2
amm
3
coursier
4
cs
5
metals
6
sbt
7
sbtn
8
scala
9
scala-cli
10
scalac
11
scalafmt

Coursier のインストールは Home Manager で管理する。 Home Manager を使っていない人は公式サイトに載っている手順に従ってインストールしよう。

1
{ pkgs, ...}:
2
3
{
4
home.packages = with pkgs; [
5
coursier
6
];
7
}

sbt や Scala コンパイラ、Metals も Home Manager で管理できるが、それらについては Coursier で管理する。

Coursier は cs コマンドとしてインストールされる。最初に sbtscalascalac といった基本的なコマンドをインストールするために次のコマンドを実行。

1
$ cs setup

これで sbt がインストールされたが問題がある。

パスが通らない。個人の環境に依るが私の環境では .profile.zprofilezsh が認識してくれないので、以下の設定を .zshrc に追加した。

1
case ${OSTYPE} in
2
darwin*)
3
export PATH="$PATH:/Users/${USER}/Library/Application Support/Coursier/bin"
4
;;
5
*)
6
# setting for linux
7
;;
8
esac

手元の環境の macOS でのみ動作する設定を入れている。 Windows や Linux を使っている人は適宜、設定を修正して欲しい。

晴れて sbt コマンドが使えるようになったので基本となるプロジェクトを生成する。ミニマムな環境は sbt new scala/scala3.g8 を実行すればよい。

1
$ sbt new scala/scala3.g8
2
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
3
SLF4J: Defaulting to no-operation (NOP) logger implementation
4
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
5
A template to demonstrate a minimal Scala 3 application
6
7
name [Scala 3 Project Template]: sample
8
9
Template applied in /private/tmp/./sample

これにより Hello, World と出力するだけのコードを含むフォルダが生成される。

ディレクトリ構造は以下のようになっている。

1
tree sample
2
sample
3
├── README.md
4
├── build.sbt
5
├── project
6
│   └── build.properties
7
└── src
8
├── main
9
│   └── scala
10
│   └── Main.scala
11
└── test
12
└── scala
13
└── MySuite.scala
14
15
7 directories, 5 files

Main.scala の中身は以下のような簡単なコード。

1
$ cat sample/src/main/scala/Main.scala
2
───────┬────────────────────────────────────────────
3
File: sample/src/main/scala/Main.scala
4
───────┼────────────────────────────────────────────
5
1 │ @main def hello(): Unit =
6
2 │ println("Hello world!")
7
3 │ println(msg)
8
4
9
5 │ def msg = "I was compiled by Scala 3. :)"
10
───────┴────────────────────────────────────────────

さっそく実行してみよう。 sample ディレクトリに移動して sbt run を実行する。

1
$ cd sample
2
$ sbt run
3
[info] welcome to sbt 1.10.5 (Azul Systems, Inc. Java 17.0.13)
4
[info] loading project definition from /private/tmp/sample/project
5
[info] loading settings for project root from build.sbt ...
6
[info] set current project to sample (in build file:/private/tmp/sample/)
7
[info] compiling 1 Scala source to /private/tmp/sample/target/scala-3.5.2/classes ...
8
[info] running hello
9
Hello world!
10
I was compiled by Scala 3. :)
11
[success] Total time: 1 s, completed 2024/11/17 19:24:17

Hello, World を実行できたし Scala 完全に理解した。

Emacs

開発は Emacs を使って行いたい。 JetBrains に課金しているので IntelliJ を使ってもいいのだが、新しい Mac mini で Emacs の動作が爆速になったので Emacs でいけるところまで試す。

現代において Language Server は不可欠なので Metals をインストールする。 Metals のインストールは公式の手順に従う。

1
(setup scala-mode
2
(:elpaca t))
3
4
(setup sbt-mode
5
(:elpaca t)
6
(:opt sbt:program-options '("-Dsbt.supershell=false"))
7
;; WORKAROUND: https://github.com/ensime/emacs-sbt-mode/issues/31
8
;; allows using SPACE when in the minibuffer
9
(substitute-key-definition
10
'minibuffer-complete-word
11
'self-insert-command
12
minibuffer-local-completion-map))
13
14
(setup lsp-metals
15
(:elpaca t))
16
17
(setup dap-mode
18
(:elpaca t)
19
(:with-mode scala-mode
20
(:hook dap-mode
21
dap-ui-mode))
22
(dap-auto-configure-mode))

lsp-mode のインストールは省略している。

これで .scala 拡張子のファイルを開くと Metals のインストールを促されるがプロンプトに従ってインストールを進めると Emacs がフリーズする。環境の問題か判断がつかなかったが Coursier で Metals をインストールすることで回避できた。

1
$ cs install metals

これにより Scala のソースコードを開いたとき Metals が認識されるのでインストールによるフリーズを回避できる。

Metals

型が見れるようになった。

おわり

Scala 3 の環境構築できたし、ブログを書きつつ学習していくぞい。


Buy Me A Coffee