• gleam/listのfoldは便利

    2024年12月11日

    この記事はGleam Advent Calendar 202411日目の記事です。

    Gleam標準ライブラリのlistモジュール(​gleam/list​)には​fold​という関数があります。

    <div class="flex justify-center border-t border-gray-200 h-15 md:h-25 mx-5 md:mx-10"> <a href="https://hexdocs.pm/gleam_stdlib/gleam/list.html#fold" class="!no-underline flex w-full bg-white rounded-lg border border-gray-200 shadow hover:bg-gray-100" title="Link Card" > <div class="flex grow flex-col"> <span class="!text-xs md:!text-base px-3 pt-2 py-0 h-20 !mt-0 !mb-0 font-bold tracking-tight text-gray-900 overflow-hidden text-ellipsis"> hexdocs.pm </span> <span class="font-xs px-3 pb-3 !mb-0 text-gray-700 text-sm max-w-48 md:max-w-full overflow-hidden whitespace-nowrap text-ellipsis"> https://hexdocs.pm </span> </div> </a> </div>

    この関数が想像以上に便利だったので紹介します。

    foldとは

    foldとは、リストの折り畳みをする関数です。 fold自体はGleam以外の言語にも提供さえていて、JSだと​reduce​メソッドでその機能が使えます。

    <div class="flex justify-center border-t border-gray-200 h-15 md:h-25 mx-5 md:mx-10"> <a href="https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce" class="!no-underline flex w-full bg-white rounded-lg border border-gray-200 shadow hover:bg-gray-100" title="Link Card" > <div class="flex grow flex-col"> <span class="!text-xs md:!text-base px-3 pt-2 py-0 h-20 !mt-0 !mb-0 font-bold tracking-tight text-gray-900 overflow-hidden text-ellipsis"> developer.mozilla.org </span> <span class="font-xs px-3 pb-3 !mb-0 text-gray-700 text-sm max-w-48 md:max-w-full overflow-hidden whitespace-nowrap text-ellipsis"> https://developer.mozilla.org </span> </div> </a> </div>

    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​モジュールにも実装されています。 こちらは遅延評価されるため、大きなリストを扱う処理に適しています。

    <div class="flex justify-center border-t border-gray-200 h-15 md:h-25 mx-5 md:mx-10"> <a href="https://comamoca.dev/blog/2024-12-09-gleam-iterator-update/" class="!no-underline flex w-full bg-white rounded-lg border border-gray-200 shadow hover:bg-gray-100" title="Link Card" > <div class="flex grow flex-col"> <span class="!text-xs md:!text-base px-3 pt-2 py-0 h-20 !mt-0 !mb-0 font-bold tracking-tight text-gray-900 overflow-hidden text-ellipsis"> comamoca.dev </span> <span class="font-xs px-3 pb-3 !mb-0 text-gray-700 text-sm max-w-48 md:max-w-full overflow-hidden whitespace-nowrap text-ellipsis"> https://comamoca.dev </span> </div> </a> </div>
    <div class="flex justify-center border-t border-gray-200 h-15 md:h-25 mx-5 md:mx-10"> <a href="https://hexdocs.pm/gleam_yielder/gleam/yielder.html#fold" class="!no-underline flex w-full bg-white rounded-lg border border-gray-200 shadow hover:bg-gray-100" title="Link Card" > <div class="flex grow flex-col"> <span class="!text-xs md:!text-base px-3 pt-2 py-0 h-20 !mt-0 !mb-0 font-bold tracking-tight text-gray-900 overflow-hidden text-ellipsis"> hexdocs.pm </span> <span class="font-xs px-3 pb-3 !mb-0 text-gray-700 text-sm max-w-48 md:max-w-full overflow-hidden whitespace-nowrap text-ellipsis"> https://hexdocs.pm </span> </div> </a> </div>

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


    ko-fi ☕GitHub Sponsors 🐙