
2025/12/14 2:00
I tried Gleam for Advent of Code
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
改訂サマリー:
著者は、7 年連続で Advent of Code のすべてのチャレンジを解決した経験を語り、2025 年に Gleam を選んだ理由を説明しています。彼らは 2025 年が 12 日(24 部)に短縮されたことにも触れています。Gleam のクリーンな構文、便利なコンパイラ診断、
echo や dict.get などの組み込みヘルパー、関数型スタイル、スムーズな言語サーバーが効率的で楽しい体験に貢献しました。しかし、標準ライブラリにはファイル I/O と正規表現がなく、外部依存 (simplifile、gleam_regexp) が必要です。また、パターンマッチは中間要素と末尾要素を同時に持つリストを分解できず、一部の解析戦略に制限がありました。著者はまた、すべてのターゲットで統一された大整数処理を望んでいます。これらの長所と短所を強調することで、他の Advent of Code 参加者や開発者に Gleam を試し、その成長するエコシステムに貢献してもらうことを期待しています。本文
毎年アドベント・オブ・コード(AoC)に挑戦しています。
過去七年間、今年を含めて全ステージの星を獲得できました。
自慢したいわけではなく、そう言うのは「なぜまた戻ってくるか」を説明するためです。
長時間続けても飽きない数少ない技術的伝統の一つであり、
締め切りがある緊張感やコミュニティの雰囲気、毎年12月に好きな言語を選んで没頭できる点が好きです。
今年は Gleam を選びました。
1 年間を短縮した構成
通常 AoC は 25 日間ですが、今年は Eric が 12 日間にしてくれたので、パート数は 50 から 24 に減りました。
「リラックスできる年」には思えましたが、実際は逆に早いペースで進むことになり、
以前よりも難しい日が多かったのですが、それでも楽しく取り組めました。
また、最初から 50 パートをこなすとツールボックスを作る余裕があるのに対し、
12 日間では パズルがまだ構築途中のツールボックスを要求してくるので、新しい言語学習にピッタリでした。
2 Gleam が AoC に合う理由
Gleam はすぐに好きになれる
シンタックスはきれいで、コンパイラが親切。エラーも抜群です。
Rust ほどではありませんが、言語自体が「テキストを解析 → 処理を重ねる → 集約する」という AoC に合ったスタイルへと自然に誘導します。
さらに パイプ が至る所にあるので好きです。
エディタ体験が予想以上に良い
LSP は思ったよりもずっとスムーズで、IntelliJ の Gleam 拡張機能は完璧でした。
https://plugins.jetbrains.com/plugin/25254-gleam-language
関数型プログラミングが好き
FP は必ずしも簡単ではありませんが、理解できれば「手順を書かない」形で解決策を記述できます。
3 Gleam のスーパー機能:echo
echo最初に惹きつけられたのは
echo。値をそのままパイプラインへ渡して表示できる プリント 文です。
list.range(0, 5) |> echo |> list.map(int.to_string) |> echo
複数箇所で値を確認したいときに、流れを壊さずに済みます。
文字列補間がなくても、
echo が多くの場面で足りました。例として LP ファイル(
glpsol 用)を作るコード:
"Minimize\n" <> " total: " <> buttons |> string.join(" + ") <> "\n\nSubject To\n"
4 グリッドパズルに便利な「オプション」
多くの AoC パズルはグリッドを扱います。
dict.get が Option を返すので、境界チェックが不要です。
fn get_neighbours(grid: Grid(Object), pos: Position) -> List(Object) { [ #(pos.0 - 1, pos.1 - 1), #(pos.0 - 1, pos.1), #(pos.0 - 1, pos.1 + 1), #(pos.0, pos.1 - 1), #(pos.0, pos.1 + 1), #(pos.0 + 1, pos.1 - 1), #(pos.0 + 1, pos.1), #(pos.0 + 1, pos.1 + 1), ] |> list.filter_map(fn(neighbour_pos) { grid |> dict.get(neighbour_pos) }) }
また
list.transpose があれば Day 6‑part 1 は転置だけで解けました。Gleam に備わっている関数が多く、
list.combination_pairs なども便利です。
5 「fold_until」こそ好きな機能
「早期終了」を安全に行える
fold_until は、パズルで頻出する 条件付き集約 に最適です。例:Day 8‑part 2 の集合マージ。
|> list.fold_until(initial, fn(acc, pair) { case done_yet { True -> Stop(new_acc) False -> Continue(new_acc) } })
6 Gleam がちょっと手間だった点
-
標準ライブラリにファイル I/O がない
Day 1 で驚きました。
を使いましたが、基本的には外部依存です。simplifile -
正規表現も別パッケージ
Day 2‑part 2 では
を追加し、次のように書きます。gleam_regexp
let assert Ok(re) = regexp.from_string("^(" <> substring <> ")+$") regexp.check(re, val)
-
リストパターンマッチングの制限
や[first, ..rest]
は書けるが、[first, second]
は不可。[first, ..middle, last]
パースをシンプルにしたいときは不便。 -
比較は明示的
比較演算子は
ではなく順序値です。bool
を書くと少し冗長になります。<=case cmp_start, cmp_end { order.Lt, _ -> False _, order.Gt -> False _, _ -> True } -
ビッグ整数と JavaScript 対応
を使うケースが出てきました。Erlang VM ではオーバーフローは起きませんが、bigi
JavaScript ターゲットだと必要になるため、
に統一できれば便利です。Int
7 XOR とビットマスクの魅力
Day 10‑part 1 が最も好き。
ライトを数値で表し、ボタンはビットマスクとして扱い、XOR で最小組み合わせを求めました。
combination |> list.fold(0, fn(acc, comb) { int.bitwise_exclusive_or(acc, comb) })
シンプルで高速。表現が多くの作業を担ってくれます。
8 glpsol を呼び出す手間
Day 10‑part 2 は逆に「外部コマンド」を使う必要がありました。
shellout.command("glpsol", ["--lp","temp.lp","-w","temp_sol.txt"], ".", []) で動き、LP フォーマット自体は美しかったです。
9 実際に役立つメモ化キー
Day 11‑part 2 のメモ化では「ノード+状態」をキーにしました。
#(neighbour, new_seen_dac, new_seen_fft)
正しくスレッドを作ると、即座に高速化できました。
10 最後のパズル:トロール問題
最後の日は “入力に対して想定外の動きがある” と感じました。
「完全に組み合わさったピースの面積がボードに収まるか」をヒューリスティックで判断しました。
heuristic_area <= max_area
11 終わりに
Gleam を選んだことを本当にうれしく思います。
標準ライブラリの限界はあるものの、パイプラインや
Option/Result が安全性を高め、list 関数群や fold_until は驚きでした。ループを書かずに関数型で書けると、解法がより明確になります。
実際のプロジェクトでも Gleam を使いたいです。ウェブサーバーを作る予定で、今からワクワクしています。来年の AoC も待ちきれません。
すべて 12 日分のソースはここにあります:
https://github.com/tymscar/Advent-Of-Code/tree/master/2025/gleam/aoc/src