この記事はGleam Advent Calendar 202411日目の記事です。
Gleam標準ライブラリのlistモジュール(gleam/list
)にはfold
という関数があります。
この関数が想像以上に便利だったので紹介します。
foldとは
foldとは、リストの折り畳みをする関数です。 fold自体はGleam以外の言語にも提供さえていて、JSだとreduce
メソッドでその機能が使えます。
foldを使うと、「リストの処理中に同一の値に参照する」みたいなコードをスッキリと表現できます。 手続き型言語でfor
と変数を用いて実装するような処理が該当すると思います。
実際の活用例
最近僕が書いたコードだと、「プレースホルダ$N
が含まれているテンプレート文字列tmpl
に対して、リストの値を順番にそのプレースホルダに適用させる」 言わばテンプレートエンジン的な動作を再現するものがあります。
import gleam/io
import gleam/list
import gleam/int
import gleam/string
pub fn main() {
let tmpl = "import gleam/io
pub fn $1() {
let $2 = \"$3 and $4\"
io.$5($2)
}"
let editors = ["main", "msg", "vim", "emacs", "println"]
list.zip(editors, list.range(1, list.length(editors)))
|> list.fold(tmpl, fn (tmpl, editor: #(String, Int)) {
let each = string.concat(["$", int.to_string(editor.1)])
string.replace(tmpl, each:, with: editor.0)
})
|> io.println
}
zip
はおなじみの通り2つのリストを1つのタプルのリストにまとめる関数です。 これにより、JSのmap
メソッドにおけるindex引数相当の処理が行なえます。
このスニペットでは明示的な型付けをほぼ行なっていませんが、唯一editor
変数のみ行なっています。 これはTupleでインデックスアクセスを行なう際にGleamコンパイラが型を検証するためです。
実行すると以下のようなGleamとして有効なコードが生成されます。
import gleam/io
pub fn main() {
let msg = "vim and emacs"
io.println(msg)
}
なお、fold
関数はgleam/list
のみならず先日紹介したgleam/yielder
モジュールにも実装されています。 こちらは遅延評価されるため、大きなリストを扱う処理に適しています。
という訳で、foldは便利という話でした。