特殊形式による式評価について

元ネタが Zenn の記事だし小ネタだし,どちらで書こうか悩んだが,今まで書いたことがない切り口な気がするので,こっち側で。

いきなりだが,以下のコードを起点に話を始めよう。

package main

import "fmt"

func main() {
    ageMap := map[string]uint{
        "Alice": 18,
        "Teles": 20,
    }
    fmt.Println(ageMap["Alice"])
    // Output
    // 18
}

このコードをみて分かるように,インデックス式 ageMap[x] の評価結果は uint 型になっている(インデックス値が存在しない場合はゼロ値がセットされる)。 まぁ,当たり前だよね。

ところが ageMap[x] の評価結果を (uint, bool) の組(tuple)で受けることもできるのだ1。 これによってコードを

package main

import "fmt"

func main() {
    ageMap := map[string]uint{
        "Alice": 18,
        "Teles": 20,
    }
    if age, ok := ageMap["Selene"]; ok {
        fmt.Println(age)
    } else {
        fmt.Println("unknown")
    }
    // Output
    // unknown
}

と書き換えることでき, ageMap[x] の評価に失敗した際の挙動を記述できる。 Go言語仕様では,これを「特殊形式(special form)」と呼んでいる(日本語が怪しいのはご容赦)。

言語仕様を眺めてみると,この特殊形式が適用可能なのは以下の3つのみのようだ。

# Expressions Normal Form Special Form
1 Index Expression on Map foo := bar[x] foo, ok := bar[x]
2 Type Assertion foo := bar.(T) foo, ok := bar.(T)
3 Receive Expression foo := <-ch foo, ok := <-ch

最初のはインデックス値 x が map にない場合に false になる。 2番目は型 T でのアサーションに失敗した場合に(panic を吐くことなく) false になる。 最後のはチャネル ch が閉じている場合に false になる。

他の式評価や関数の返り値ではこのようなことはできない。 たとえば slice のインデックス式評価に特殊形式はない。 インデックス値が範囲外なら単に panic が投げられるだけだ。

実は,特殊形式が上の3つの式評価でしか適用されないというのが分からなくて「どうして普通の関数の返り値とかではできないんだろう」とひとしきり悩んだことがあったのだった。 言語仕様を読めっての(笑)

ブックマーク

参考図書

photo
プログラミング言語Go
アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)
丸善出版 2016-06-20 (Release 2021-05-21)
Kindle版
B094PRR7PZ (ASIN)
評価     

Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想はこちら

reviewed by Spiegel on 2021-05-22 (powered by PA-APIv5)


  1. 厳密には,特殊形式 (T, bool) の2要素目は型付けなしの真偽値(untyped boolean)に評価される。「型付けなし(untyped)」というのは Go 特有の概念だそうで,具体的な型が決定される前の状態を指す。たとえば,定数 math.Pi は10進数64桁の小数点数で定義されている。型付けなし定数については『プログラミング言語Go』の3.6.2章にも解説がある。 ↩︎