コンティニュエーションを用いた効果抽象化

2026/07/16 19:41

コンティニュエーションを用いた効果抽象化

RSS: https://news.ycombinator.com/rss

要約

日本語訳:

本テキストは、Gleam が継続(continuations)を用いて、同期および非同期の動作を単一の再利用可能なビジネスロジック構造に統合する方法を説明しています。1994 年の論文「Monads を表現する」(Representing Monads)における Filinski の定理を適用することで、異なる Fetch タイプがそれぞれの実行モデルに関わらず、同一の処理ステップを共有できることが証明されます。これは、「前後(before and after)」の枠組みを提供することで具体的な計算の詳細から抽象化し、汎用的な関数が様々な結果タイプを円滑に扱えるようにするためです。Filinski の Monad に関する理論的基盤に基づいているこの抽象化は、同一のコードベース内で故障可能な同期ロジック(Result)と非同期のプロミス(asynchronous promises)の両方をカバーします。実際には、呼び出し側が特定の

fetch
実装を通じて具体的な計算タイプを定義し、ランナーが継続から最終値を引き出すことを管理します。この関心の分離により、核心となるビジネスルールはネットワークコールやエラー処理などの具体的なメカニズムから隔離されます。その結果、開発者はデータソースを交換してもロジックを書き換える必要がなく、将来の実行は単に適切なインタープリター(例:単純な同期ランナーまたは非同期プロミスハンドラー)を選択するだけで済むようになります。

(元のサマリーは高品質であり改善を要しませんでした。そのため、流れを良くするための少々のスタイル調整を加えて再記載しています。)

本文

継続(Continuation)による計算効果の一般化:Gleam を中心に

失敗の可能性を伴う計算を値として表現することは、信頼性の高いプログラムを書くための強力なツールです。「Result」型は期待される成功の結果や失敗の原因を包み込み、関数が

Result
を返すことで、呼び出し側が失敗を考慮することを強制します。同様に、非同期計算を表す**「Promise」**も、値へのアクセス前に計算完了を待つことを強制する仕組みです。

これらの型は特定の計算詳細を追跡しますが、計算の詳細をさらに一般化するには**継続(Continuation)**を使います。継続は、「値が得られたらどう処理すべきか」を指定する仕組みであり、Filinski の論文『Representing Monads』ではこれがあらゆるモナド(Result や Promise など)を表現できることが示されています。

ここでは数学的な証明には触れず、Gleam プログラミング言語における継続の活用方法を紹介します。


ありきたりな例:同期計算

まずはキーに対する値を取得する単純な

fetch
関数を考えます。この関数は文字列型のキーを受け取り、文字列型の値を返します。

fn fetch(key: String) -> String {
  "yes"
}

ビジネスロジックでは、固定されたキーリストに対してそれぞれ大文字に変換し、関連する値の文字数を返す必要があります。

pub fn simple_func(fetch: fn(String) -> String) -> List(Int) {
  let keys = ["a", " b"]
  list.map(keys, fn(key) {
    let key = string.uppercase(key)
    let value = fetch(key)
    string.length(value)
  })
}

エンタープライズへ進む:計算効果の多様化

事業部門の拡大に伴い、データは様々なストレージに保存されるようになり、以下のような複雑な要件が生じました。

  • 失敗の可能性: すべてのキーがヒットしない場合がある(
    Result
    が必要)。
  • 非同期性: データストアへのアクセスが時間がかかる(
    Promise
    が必要)。

これに対応するために、ビジネスロジックの変更なしに新しい関数を作成せざるを得なくなりました。

1. 失敗の可能性を扱う (
Result
)

pub fn fallible_func(fetch: fn(String) -> Result(String, Nil)) -> Result(List(Int), Nil) {
  let keys = ["a", " b"]
  list.try_map(keys, fn(key) {
    let key = string.uppercase(key)
    use value <- result.map(fetch(key))
    string.length(value)
  })
}

2. 非同期計算を扱う (
Promise
)

pub fn async_func(fetch: fn(String) -> Promise(String)) -> Promise(List(Int)) {
  let keys = ["a","b"]
  list.map(keys, fn(key) {
    let key = string.uppercase(key)
    use value <- promise.map(fetch(key))
    string.length(value)
  })
  |> promise.await_list
}

課題:抽象化の欠如

この時点で、私たちは3 つの実装を持つことになりました。各バージョンでビジネスロジックは同一ですが、関数の型が異なり、ロジックを再利用できません。

さらに悪化するのは、両方の要件(失敗の可能性 + 非同期)を満たす

Promise(Result(...))
タイプの
fetch
が必要になった場合、4 つ目の実装を作らざるを得なくなる点です。

ここで重要なのは、実装間で変化する点は「計算の種類(同期的/失敗あり/非同期)」のみであり、共通しているのは:

  • キーを作成する方法
  • 値が提供されたときの処理ロジック

この「事前」と「事後」の関係性を表現するのが継続です。


欠缺した抽象化:継続による一般化

計算の種類に関わらず、共通するビジネスロジックを抽出し一般化します。

pub type Continuation(t, a) =
  fn(fn(a) -> t) -> t

ここで、

  • a
    : 継続が包み込んでいる値のタイプ。
  • t
    : (現時点で未指定の)計算の詳細(成功、失敗、非同期など)。

次のバージョンでは、

fetch
Continuation(t, String)
を返すように設計します。

import midas/continuation.{type Continuation as K}

pub fn task(fetch: fn(String) -> K(t, String)) -> K(t, List(Int)) {
  let keys = ["a","b"]
  continuation.each(keys, fn(key) {
    let key = string.uppercase(key)
    use value <- continuation.then(fetch(key))
    continuation.return(string.length(value))
  })
}

この

task
計算の種類を一般化しています。呼び出し側は、提供する
fetch
の実装を通じて
t
の具体的なタイプを選択し、最終的な値を抽出するためのコールバック関数を提供します。このようなタスクの呼び出し元は通常「ランナー(Runner)」または「インタプリタ」と呼ばれます。

このアプローチにより、ロジックと効果を分離することが可能になります。


ランナーの実装例

以下に、同じ

task
を使いながら異なる効果を持つ「ランナー」を作成する方法を示します。

1. シンプルなランナー(同期)

fetch
が失敗せず常に文字列を返す場合、継続はアイデンティティ関数でunwrap できます。最終的な値はそのままの型になります。

import midas/continuation

fn run_simple(task) -> List(Int) {
  let fetch = fn(_key) { continuation.return("yes") }
  
  task(fetch)(fn(x) { x })
}

// テスト例
fn run_simple_test() {
  let assert [3, 3] = run_simple(task) // "yes" の長さは 3
}

2. 失敗の可能性のあるランナー (
Result
)

fetch
Result
を返す場合、最終的なコールバックも同様に結果型を返す必要があります。値が存在すればその値を受け取り、存在しない場合はエラーを返します。

fn run_fallible(keys, task) -> Result(List(Int), Nil) {
  let fetch = fn(key) {
    fn(then) {
      case dict.get(keys, key) {
        Ok(value) -> then(value)
        Error(Nil) -> Error(Nil)
      }
    }
  }

  task(fetch)(Ok) // 成功のケースを継続する
}

fn run_fallible_test() {
  let assert Error(Nil) = 
    run_fallible(dict.from_list([#("A", "Apple")]), task)
  
  let assert Ok([5, 6]) = 
    run_fallible(dict.from_list([#("A", "Apple"), #("B", "Banana")]), task)
}

3. 非同期ランナー (
Promise
)

fetch
が非同期の場合、ランナーは
t
Promise(List(Int))
に選択し、最終的なコールバックで
promise.resolve
を使用します。

fn run_async(task) {
  let fetch = fn(_key) {
    fn(then) {
      use Nil <- promise.await(promise.wait(100)) // 遅延シミュレーション
      then("slow")
    }
  }

  task(fetch)(promise.resolve)
}

fn run_async_test() {
  use v <- promise.await(task |> run_async())
  // [slow, slow] になるが、実際には文字列なのでテスト値は異なる可能性があることに注意
  let assert [4, 4] = v // ※ここでは例用の数値だが、実際には "slow" の長さ 4
}

まとめ

task
という関数を使い込むことで、ビジネスロジックの単一の定義を手に入れることができました。これは「値がどのように取得されたとしても、それをどう扱うべきか」を記述するものです。

  • ランナー: 値を取得する方法(同期/失敗あり/非同期など)を決定します。
  • 継続: ビジネスロジック自体は一度定義しきり、後方から必要な効果を与えます。

このスタイルで記述された任意のタスクは、同じランナーを使用できます。ロジックと効果を分離することが、継続が私たちに与える強力な抽象化です。

※著者は現在、EYG という実験的プロジェクトで言語やツールの改善に取り組んでおり、不定期に発行するニュースレターにて進捗報告を行っております。

同じ日のほかのニュース

一覧に戻る →

2026/07/16 23:46

Kimi K3:オープン・フロンティア・インテリジェンス

## Japanese Translation: Kimi K3 は、ネイティブビジョン機能と 100 万トークンのコンテキストウィンドウを備えた、開かれた 2.8T パラメータモデルであり、同クラスにおいて初めてこの規模に達したものです。Kimide Delta Attention (KDA)、Attention Residuals (AttnRes)、Stable LatentMoE(896 のエキスパートのうち 16 を有効化)を活用し、Kimi K2 に比べて約 2.5 倍のスケーリング効率向上を実現しています。API、モバイルアプリ(iOS、Android、HarmonyOS)、デスクトップアプリ(Windows/Mac v3.1.0 以降)、ターミナル(Kimi Code)、および Kimi Work プラットフォーム経由で現在利用可能です。完全なモデル重みは 2026 年 7 月 27 日にリリース予定です。 発売時はデフォルトで"max"の思考努力で動作し、将来のアップデートで低い effort と高い effort のモードが個別に導入される予定です。全体的一般ベンチマークでは Claude Fable 5 や GPT 5.6 Sol などのプロプライエタリモデルに劣りますが、「max」推論(temperature = 1.0, top-p = 1.0)を使用する際は、評価スイート内でテストされた他のすべてのモデルで一貫して高スコアを獲得しています。NVIDIA H200 上での Kernel Optimization テストでは、Claude Fable 5 と競合的な性能を誇り、Opus 4.8、GPT 5.6 Sol、GPT 5.5 を大幅に上回っています。また、NanoTriton を成功裏に構築しており、これは Triton や torch.compile に匹敵するかそれ以上のパフォーマンスを発揮しながら、エンドツーエンドの nanoGPT 訓練を継続可能なカスタム GPU コンパイラです。 ドメイン固有のベンチマークでは Kimi K3 が内部指標で優位を示しています:DeepSWE(67.5)、Program Bench(77.8)、Terminal Bench 2.1(88.3)。顕著なケーススタディには、48 時間以内にナノモデルを供給するチップの自律的デザイン(シミュレーションにおいて 100 MHz のタイミングクロージャー達成および>8,700 トークン/秒のデコードスループット達成)、ならびに 20 以上の論文を相互検証し Python コード 3,000 行以上を生成することで複雑な天体物理学研究タスクを数週間から約 2 時間へと短縮する実績が含まれます。 Kimi Work には、永続的な視覚インタラクションのためのウィジェットおよびダッシュボード、そしてネイティブ動画編集機能を搭載した新機能が追加されました。モデルは API 経由で利用可能で、入力/出力それぞれに対し MToken あたり 0.30 米ドル・3.00 米ドル・15.00 米ドルの料金体系を適用しています。制限事項としては、思考履歴への感受性(特定のハネス互換性を必要とする例:Kimi Code など)、および曖昧なタスクに対して過度の能動性を示す傾向があり、これらには明示的な振る舞い制約が必要です。

2026/07/17 5:18

LM Studio Bionic:オープンソースモデル用の AI エージェント

## Japanese Translation: LM Studio Bionic は、機密データを厳格な「ゼロデータ保持ポリシー」に従って安全に処理するために設計された専用のワークアプリケーションとして発売されました。このポリシーにより、文書やコードがより広範なシステムの学習に使用されることは一切ありません。プラットフォームは柔軟な実行環境を提供しており、ユーザーは自身のハードウェア上でローカルにタスクを実行できるほか、LM Link プロトコルを介して接続したり、処理直後にリクエストを削除する安全なクラウドを利用したりすることができます。ただし、クラウドモデルへのアクセスには LM Studio アカウントの作成および課金設定が必要です。主な機能としては、Mistral AI Voxtral を活用したオフライン音声認識と、GLM 5.2 や Kimi K2.7 Code のようなモデルを用いた高度なコーディング能力(ローカルコードベースの検査、説明、編集)が挙げられます。文書処理には PDF スライド表計算シートなどが含まれます。すべてのファイルはサンドボックス化された「Work project」環境内で安全に扱われます。今後のアップデートでは、ジョブごとに特定モデルを選択するための Bionic エージェントおよび、文書の変更を元に戻せる自動チェックポイント機能が導入されます。このソリューションは、ユーザーのプライバシーを犠牲にしてモデル学習を行うプラットフォームとは異なり、組織がコードベースの検査や PDF の分析などを実行しながら、コストを正確に管理でき、安心して作業を進められるよう支援します。

2026/07/17 1:06

Microsoft Comic Chat がオープンソース化されました

## Japanese Translation: Microsoft は、1990 年代に登場し、テキストとイラスト化された漫画パネルを組み合わせた画期的なチャットクライアント「Comic Chat」のオープンソース版を正式に公開しました。このソフトは、ダビッド・DJ・カルランダー氏により Microsoft Research で 1995 年に構想され、Visual C++ 4.0 と MFC を用いて開発されました。同年に Internet Explorer 3 に、その後 Windows 98 にも組み込まれ、24 の言語に対応してリリースされました。「Comic Sans」フォントを最初にスピーチバブル会話の文脈で採用したことで同フォントを広めたのもこの製品です。実際のチャットセッションのトランスクリプトから、漫画家 Jim Woodring 氏が独自のビジュアル資産を作成し、システムは会話を示す手がかりを読み取り、自動的にポーズ、表情、ジェスチャー、パネルレイアウトを生成しました。これは SIGGRAPH '96 の論文で「自動イラストレーション構築の実験」として記述されています。Comic Chat は現代のリアクション、スタicker、GIF、AI 生成コンテンツ、そしてビデオが登場する以前の技術であり、それらの機能を先駆けとしたノスタルジックなアーチファクトとして機能します。オープンソースリリースの一環として、Microsoft は元々の C++/MFC コードを現在の Visual Studio ツールおよび高解像度 Windows システムで動作させるための AI による近代化試みを含めており、これらは洗練されたリイシューではなく、現代的な IRC サーバーとの互換性を示す実用サンプルとして提示されています。この取り組みはソフトウェアの歴史を保存すると同時に、開発者、歴史家、愛好家がソースコードを探索し、実験し、1990 年代の技術を基にした新たな形で創作できるよう促しています。