Scala で「1: 1」を書けるようになった話

この記事は [Scala Advent Calendar 2020](https://qiita.com/advent-calendar/2020/scala) の 15 日目の記事です．

## はじめに

Scala 2.13 で `1: 1` という一見不可解な式を書けるようになったので紹介します．

```
% scala
Welcome to Scala 2.13.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_191).
Type in expressions for evaluation. Or try :help.

scala> 1: 1
val res0: Int = 1
```

この新しい構文は，シングルトン型をリテラルで書けるようにしたものです．
[SIP-23](https://docs.scala-lang.org/sips/42.type.html) で提案され，Scala 2.13 で導入されました．

Scala のシングルトン型に関して，日本語の情報があまり見つからないので，記事にしておこうと思います．

## シングルトン型とは

シングルトン型 (singleton type) は，ちょうど 1 つの値からなるデータ型です．

シングルトン型なんて聞いたことないという人も，知らず知らずのうちに使っているかもしれません．
例えば以下のように `final val` で定数を定義すると，その型はシングルトン型になります．

```scala
final val one = 1
// one: Int(1) = 1
```

ここで，`Int(1)` がシングルトン型です．
`Int(1)` は，唯一の値 `1` からなるデータ型を表します．

シングルトン型の概念自体は Scala 2.12 以前から存在していて，言語仕様書でいうと §3.2.1 "Singleton Types" に説明があります．
たしか，コップ本にも少しだけ説明が書かれてたはずです．

 * [3.2.1 Singleton Types](https://scala-lang.org/files/archive/spec/2.12/03-types.html#singleton-types) - Scala Language Specification Version 2.12

一応，Scala 2.12 以前でも `.type` をつけることでシングルトン型を表すことはできました．
ただ，この形式を使えるのは参照型のパスに限られています．

```scala
val s = "hello"
// s: String = hello

val t: s.type = s
// t: s.type = hello
```

他に Scala 2.12 以前でシングルトン型を扱う方法には，ライブラリを使う方法がありました．
例えば [shapeless](https://github.com/milessabin/shapeless/) の `Witness` を使うことで，シングルトン型を表現できます．

```scala
import shapeless._
import shapeless.syntax.singleton._

val one: Witness.`1`.T = 1.narrow
// one: Int(1) = 1
```

## リテラルベースのシングルトン型

Scala 2.12 以前は，コンパイラの出力には `Int(1)` のようなシングルトン型の型注釈が現れるのに，コードとしては書けないという，もやもやがありました．

```scala
final val one = 1
// one: Int(1) = 1

val one: Int(1): 1
// <console>:1: error: ';' expected but '(' found.
//        val one: Int(1): 1
//                    ^
```

言ってみればセマンティクスにシンタックスが追いついていなかったわけですが，Scala 2.13 でこの溝を埋める改善が行われたわけです．

Scala 2.13 に SIP-23 が取り込まれたことで，リテラルでシングルトン型を表せるようになりました．
この例のように，一見データ型には見えないリテラル `1` によって，シングルトン型を表せるようになりました．

```scala
val one: 1 = 1
// one: 1 = 1
```

言語仕様上は，`.type` によるシングルトン型の表現を "シングルトン型" (singleton type)，リテラルによるシングルトン型の表現を "リテラル型" (literal type) と呼び分けています．
また，シングルトン型とリテラル型を包括する概念として "安定型" (stable type) が再定義されています．

* [3.2.1 Singleton Types](https://scala-lang.org/files/archive/spec/2.13/03-types.html#singleton-types) - Scala Language Specification Version 2.13
* [3.2.2 Literal Types](https://scala-lang.org/files/archive/spec/2.13/03-types.html#literal-types) - Scala Language Specification Version 2.13
* [3.2.3 Stable Types](https://scala-lang.org/files/archive/spec/2.13/03-types.html#stable-types) - Scala Language Specification Version 2.13

他に，型注釈の構文拡張に限らない変更もいくつか導入されています．
一例として，型引数の上限境界に `Singleton` を指定することで，シングルトン型が推論されるようになりました．

```scala
type Id[A] = A
def narrow[T <: Singleton](x: T): Id[T] = x

narrow(1)
// res1: Id[1] = 1
```

これは SIP-23 からの例です．
型引数の上限境界が `Singleton` であるために，型引数 `T` が `Int` でなく `1` と推論されていることがわかります．
Scala 2.12 以前は，型引数の推論に失敗して型エラーが発生していました．

## まとめ

Scala 2.13 で導入されたリテラルベースのシングルトン型について眺めてみました．
「どこで使うの？？」という感じですが，型注釈にリテラルが使われている不思議な光景を見かけたら，シングルトン型のことを思い出してあげてください．

## 参考文献

* [SIP-23 - LITERAL-BASED SINGLETON TYPES](https://docs.scala-lang.org/sips/42.type.html)
* [Scala Language Specification Version 2.13](https://scala-lang.org/files/archive/spec/2.13/)
* [時代はリテラルレベルプログラミングだ!](https://xuwei-k.hatenablog.com/entry/20140718/1405581087) - xuwei-k's blog