• gleam/listのfoldは便利


    この記事は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
    }

    Playground

    zipはおなじみの通り2つのリストを1つのタプルのリストにまとめる関数です。 これにより、JSのmapメソッドにおけるindex引数相当の処理が行なえます。

    このスニペットでは明示的な型付けをほぼ行なっていませんが、唯一editor変数のみ行なっています。 これはTupleでインデックスアクセスを行なう際にGleamコンパイラが型を検証するためです。

    実行すると以下のようなGleamとして有効なコードが生成されます。

    import gleam/io
    
    pub fn main() {
      let msg = "vim and emacs"
      
      io.println(msg)
    }

    Playground

    なお、fold関数はgleam/listのみならず先日紹介したgleam/yielderモジュールにも実装されています。 こちらは遅延評価されるため、大きなリストを扱う処理に適しています。


    という訳で、foldは便利という話でした。


    ko-fi ☕GitHub Sponsors 🐙