Goのtype宣言はすごい
Goのtype宣言を使うと、特定の型を元にして別の型を定義できる。
一番単純な例: 基本データ型から別の名前の型を作る
type Foo int var x Foo x = 18 fmt.Printf("%T\n", x) // => main.Foo
これはわかる。intからFooという別の型を作るだけである。
関数に型名を付与する使い方
ところが、このGoのtypeってやつは関数にも利用できることに驚いた。
type NumFunc func(xs []int) int
これは全然okで、構文エラーにはならない。
つまり、
// intのスライスを引数に取って合計値を「文字列で」返す func NumString(xs []int) string { acc := 0 for i := 0; i < len(xs); i++ { acc += xs[i] } return strconv.Itoa(acc) } // intのスライスを引数に取って合計値を返す func Sum(xs []int) int { acc := 0 for i := 0; i < len(xs); i++ { acc += xs[i] } return acc } type NumFunc func(xs []int) int var fn NumFunc fn = Sum // これはok // fn = NumString // これはコンパイルエラー // cannot use NumString (type func([]int) string) as type NumFunc in assignment
このように、関数を任意の型として宣言し、所定のシグネチャを守る関数しか代入できなくさせられる。
動くコードはhttps://play.golang.org/p/Em_52ORgYqこちら
関数のファクトリを簡潔に記述する
そもそもこの話に興味を持ったのは、Pythonのデコレータに相当することをGoでやる方法を考え始めたときだった。
Pythonのデコレータは関数のファクトリだから同じことをすれば良いのだが、最初以下のような安直なやり方でしか書けないと思っていた。
package main import "fmt" func main() { var foo = func(x int) int { return x + 5 } var deco = func(fn func(int) int) func(int) int { return func(x int) int { return fn(x) } } var fn = deco(foo) fmt.Println(fn(100)) // => 105 }
とにかくdecoの定義の部分がひどい。
「func(int) int」という記載が3回も並ぶと実に読みにくい・・
なので、この重複の多い部分をtypeでくくり出す。
package main import "fmt" func main() { type F func(x int) int deco := func(fn F) F { return func(x int) int { return fn(x) } } var fn = deco(func(x int) int { return x + 5 }) fmt.Println(fn(100)) // => 105 }
すると、decoの記載がだいぶスッキリするじゃないか。というところでこのtypeってやつはすごいと思ったのであった。