Goの定数がヤヤコシイ話

## 問題

以下のコードを実行するとどうなるか？

```go
package main

import "fmt"

func main() {
	const c = 2i * 3i
	var v int = c
	fmt.Printf("%T/%T", c, v)
}
```

 1. コンパイルエラー
 2. 実行時panic
 3. `int/int`が出力される
 4. `complex128/int`が出力される

※[14日目の記事][goquiz-14]で関連する問題が出題されています。先に見ておくといいかも:upside_down: 

![scthinkingtime.png](https://qiita-image-store.s3.amazonaws.com/0/14550/c2dd4130-d1bd-0f93-2d03-eb68e7ad6e5a.png)

## 解答

<details>
<summary>解答</summary>
<div>
**4. `complex128/int`が出力される**
</div>
</details>

  * [Go Playgroundで試す](https://play.golang.org/p/4exzTJAaCIj)

## 解説

Goにおける**定数**の扱いは独特で理解しづらいものです。本問題の要点は次の2つです。

 1. Goには**型無し**(untyped)の定数がある
 2. 「型無し」といっても、Goの型が定まってないだけで、実は「真偽値」「整数」「複素数」などの**“種別”がある**

定数の“種別”[^kind]は以下のものがあります。

[^kind]: “種別”は本記事で仮に用いている用語であり、公式のGoの用語ではありません。

> There are *boolean constants*, *rune constants*, *integer constants*, *floating-point constants*, *complex constants*, and *string constants*. Rune, integer, floating-point, and complex constants are collectively called *numeric constants*.<br>([Constants][constants])

すなわち、“数値の定数”には「**ルーン定数**(rune constant)」「**整数定数**(integer constant)」「**浮動小数点定数**(floating-point constant)」「**複素数定数**(complex constant)」の4つの“種別”があります。

### 詳細

実際に`main()`の実行を追ってみます。

```go:1行目
	const c = 2i * 3i
```

`2i`と`3i`は**虚数リテラル**(imaginary literal)です。リテラルが定数として扱われるのは当然ですが、Goではこの場合特に「**型無し**の定数」になります。

> Literal constants, `true`, `false`, `iota`, and certain constant expressions containing only untyped constant operands are untyped.<br>([Constants][constants])

ただ「型無し」といっても先述の通り“種別”はあります。`2i`や`3i`は（そもそも値が実数でないので[^imag-literal]）「型無しの複素数定数」となります。

[^imag-literal]: `0i`も「型無しの複素数定数」と扱われるようなので、要するに「虚数リテラルは必ず複素数定数を表す」ということなのでしょう。規格では“An imaginary literal represents the imaginary part of a complex constant.”([Imaginary Literals][imaginary-literals])という記述しかなくてアレなんですが。

`2i * 3i`は「型無しの複素数定数」同士の乗算なので、結果も同じく「型無しの複素数定数」となります。値は−6なのですが整数定数にはならないことに注意しましょう。

> Any other operation on untyped constants results in an untyped constant of the same kind; that is, a boolean, integer, floating-point, complex, or string constant.<br>([Constant expressions][constant-expressions])

`const c = …`の定数宣言では`c`に型を付けていないため、`c`も「型無しの複素数定数」（値は−6）となります。

> If the type is omitted, the constants take the individual types of the corresponding expressions. If the expression values are untyped constants, the declared constants remain untyped and the constant identifiers denote the constant values.<br>([Constant declarations][constant-declarations])

```go:2行目
	var v int = c
```

int型変数`v`の初期値に`c`を指定したので、実質的に「`c`の`v`への代入」が発生します。従って、代入可能性(assignability)が問題になりますが、右辺が「型無し定数」である場合は表現可能性([representability])のみが要件になります。

> A value `x` is *assignable* to a variable of type `T` ("`x` is assignable to `T`") if one of the following conditions applies:
>
> - …(略)…
> - x is an untyped constant representable by a value of type T.
>
> ([Assignability][assignability])

数値の表現可能性では「数値そのもの」だけが問題になります。今の場合、「−6」はintで表される整数値であるため、「複素数定数である」ということは無関係で、この代入は可能です。

ちなみに、ここでもし右辺が「型付き」（定数でない場合も含む）である場合は、左辺の型と一致していることが要求されます。つまり以下の例[^explicit]はコンパイルエラーになります。

[^explicit]: `float32(c)`は「float32型の浮動小数点定数の−6」となります。この式自体が合法なのは「−6がfloat32型で表現可能である」ためです。

```go
	var v int = float32(c) // エラー:cannot use float32(c) (type float32) as type int in assignment
```

最後の行になりました。

```go:3行目
	fmt.Printf("%T/%T", c, v)
```

ここでは`c`と`v`という2つの式の型を考える必要があります。`v`は当然intとなりますが、「型無し」の`c`はどうすればいいでしょうか。このように[^detail]、「型無し」の定数に「他に制約がない状況」で型を与えないといけない場合は、「**デフォルト型**」（default type）が適用されることになります。

[^detail]: 詳しくいうと：`fmt.Printf`の第2引数以降は`interface{}`型の可変長引数であるため、実質的に「`c`を`interface{}`型変数に代入する」ことになります。変数が持つ値は常に型付きであり、かつ、`interface{}`型はあらゆる具象型により実装され得るので、「他に制約がない状況で型を与えないといけない」に該当します。

> The default type of an untyped constant is `bool`, `rune`, `int`, `float64`, `complex128` or `string` respectively, depending on whether it is a boolean, rune, integer, floating-point, complex, or string constant.<br>([Constants][constants])

つまり、引数の`c`は「complex128型の−6」として関数に渡されます。従って、`complex128/int`が出力されることになります。

## まとめ

 1. Goには**型無し**(untyped)の定数がある
 2. 「型無し」といっても、Goの型が定まってないだけで、実は「真偽値」「整数」「複素数」などの**“種別”がある**

**Goの定数はヤヤコシイ**:frowning2:

[assignability]: https://golang.org/ref/spec#Assignability
[constant-declarations]: https://golang.org/ref/spec#Constant_declarations
[constant-expressions]: https://golang.org/ref/spec#Constant_expressions
[constants]: https://golang.org/ref/spec#Constants
[imaginary-literals]: https://golang.org/ref/spec#Imaginary_literals
[representability]: https://golang.org/ref/spec#Representability
[goquiz-14]: https://qiita.com/shumon_84/items/1183fee619795c022fb9