samber / lo はGoperである私を愚直なfor もしくは筋肉信仰から救ってくれるのか?

はじめに

Go 1.18 がリリースされました。Go 1.18でシュッとGenerics を手軽に良さを実感する方法としてsamber/lo があります。 もちろん、Tutorial: Getting started with generics完全に理解できるならそちらの方が良いですし、これを終わった後でやることも推奨です。その他のリリースパーティ勉強会もとても勉強になりますが とにかく、samber/lo 便利なので紹介させてください!!!!!!!

go.dev

今回はとても大きな変更です。Generics が入りました。構成としては2つ。

  • Type parameter
  • Type sets

参考資料

DevFest Tokyo 2021 でmattn さんが発表したスライド&動画がとても分かりやすいので是非、見てみてください。

docs.google.com

www.youtube.com

全てfor 文で解決するのか?- そう、全て筋肉が解決してくれる

Golang にはuniq メゾットのようなものがなく、重複のある slice に対して独自に処理を実装しなければいけなかった。 愚直にfor を回すの結果として最速だからである。

arr := []string{"Samuel", "Marc", "Samuel"}
m := map[string]bool{}

for _, ele := range arr {
    if !m[ele] {
        m[ele] = true
        uniq = append(uniq, ele)
    }
}

fmt.Printf("%v", uniq) // ["Samuel", "Marc"]

Go Playground - The Go Programming Language

どういうことかというと、重複キーがあるので、同様のキーを持つmapの場合は新しく値を上書きしないみたいな処理を書かなければならなかった。 m["Samuel"] = true は一度目はこれが呼ばれるけど、二度目はすでにtrueなので if句の中に入ってず、resultにSamuelが二度入ることがないという様な仕組みです。 とにかく、全てをfor で扱い全ての型を制御するマッチョでした。

ema-hiro.hatenablog.com

全てfor 文で解決するのか?- samber/lo とか?

Golang にはuniq メゾットのようなものがなく、重複のある slice に対して独自に処理を実装しなければいけなかったがsamber/lo というプロジェクトでは Go 1.18 のGenerics を使うことによってreflect より早くforとも遜色なく動作するヘルパーを提供します。他にもいくつもの ヘルパー がありますが今回はuniq のみ紹介します。

pkg.go.dev

package main

import (
    "fmt"
    "github.com/samber/lo"
)

func main() {
    arr := []string{"Samuel", "Marc", "Samuel"}
    names := lo.Uniq[string](arr)   
    fmt.Println(names) // []string{"Samuel", "Marc"}
}
uniqValues := lo.Uniq[int]([]int{1, 2, 2, 1})
// []int{1, 2}

実装をみるとこんな感じでmapと空のstructを使う方法でuniq が実装されている。

lo/slice.go at v1.10.1 · samber/lo · GitHub

func Uniq[T comparable](collection []T) []T {
    result := make([]T, 0, len(collection))
    seen := make(map[T]struct{}, len(collection))
    for _, item := range collection {
        if _, ok := seen[item]; ok {
            continue
        }
        seen[item] = struct{}{}
        result = append(result, item)
    }
    return result
}

とにかくfor で愚直に回す言語から多少はスマートな解決ができる様になった(もしくは今後、期待ができる様になった)。

最後に

この記事を読んで興味が湧いたら元のProposalTutorial: Getting started with generics を読んでみてください。自分も何度かやってみて読んでみて使える様になりたいと思ってます。また、Go本体にも機能として追加される日を楽しみしてます。

github.com