新しいC++バックエンド for `ocamlc`

2026/04/02 8:35

新しいC++バックエンド for `ocamlc`

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

要約

日本語訳:

新しいC++バックエンドが

ocamlc
に追加され、インクリメントされていない C ランタイムと外部関数インタフェースを置き換えました。著者は、ユーザーが指定した上限まで素数を生成するプログラムでその使用例を示しています。このプログラムは OCaml の List モジュールの一部を純粋に関数型スタイルで再実装しています。プログラムは
primes.cpp
という慣用的な C++ コードへ翻訳され、
Cons
I
ifthenelse
などのテンプレートメタプログラミング構造を含みます。
g++ -Dlimit=100 primes.cpp
でコンパイルすると、
print
が型ではないためにコンパイラー風エラーが発生し、出力形式は古い C プリプロセッサのエラー(OCaml の
::
の代わりにネストされた
Cons<hd, tl>
)を模倣します。生成されたコードはデフォルトテンプレート深度を増やす (
-ftemplate-depth=999999
) ことでのみ大きな上限を扱うことができます。著者のマシンでは、
limit = 10000
を実行すると約 30 秒で 10000 以下のすべての素数が出力され、約 11 GiB のメモリを使用します。clang++ は遅く、セグフォールトする可能性があります。アルゴリズム自体は非効率的です——コンテナライブラリからの優先度付きキュー/レフトヒープに基づく改良された純粋関数型素数生成器は、同じ上限で実行時間を約 8 秒、メモリ使用量を約 3.1 GiB に削減します。今後の作業では、このアプローチを他言語へ拡張することが目標です;Rust は部分的な実装特殊化がサポートされれば OCaml プログラムを実行できるようになります。

この改訂された要約は、すべての主要ポイントを反映し、不当な推測を避け、明確な主旨を提示し、あいまいまたは混乱を招く表現を排除しています。

本文

このパッチの概要

このパッチは ocamlc に新しい C++ バックエンドを追加し、
ランタイムや FFI で現在使用されている「インクリメントされない」C を改善します。
以下に示すサンプルプログラムは、ユーザーが指定した上限までの素数を計算します。

module List = struct
  let rec filter p = function
    | [] -> []
    | x :: l ->
        if p x then x :: filter p l else filter p l

  let rec init i last f =
    if i > last then []
    else f i :: init (i + 1) last f
end

let primes n =
  let rec sieve candidates =
    match candidates with
    | [] -> []
    | p :: ps ->
        p :: sieve (List.filter (fun n -> n mod p <> 0) ps)
  in
  sieve (List.init 2 n (fun i -> i))

let main ~limit = primes limit

このプログラムを C++ に変換するには、次のようにします。

ocamlc -cpp -o primes.cpp <source.ml>

すると primes.cpp が生成されます。

primes.cpp
は OCaml のコードを「読みやすく」C++ へ翻訳したものです。


primes.cpp
に出力された C++ コード

#ifndef limit
#error "Parameter limit missing"
#include <stop_after_error>
#endif

template <typename...> struct Cons;
template <int tag, typename...> struct Cons_;
template <int n>
struct I {
  static constexpr int tag = 1000;
  static constexpr bool nonzero = (n != 0);
  static constexpr int val = n;
};

struct EXCEPTION {};

template <typename f0_, typename f1_>
struct Cons<f0_, f1_> {
  static constexpr int tag = 0;
  static constexpr bool nonzero = true;
  static constexpr int val = -1;
  typedef f0_ f0;
  typedef f1_ f1;
};

template <int tag_, typename f0_, typename f1_>
struct Cons_<tag_, f0_, f1_> {
  static constexpr int tag = tag_;
  static constexpr bool nonzero = true;
  static constexpr int val = -1;
  typedef f0_ f0;
  typedef f1_ f1;
};

template <typename f0_, typename f1_, typename f2_>
struct Cons<f0_, f1_, f2_> {
  static constexpr int tag = 0;
  static constexpr bool nonzero = true;
  static constexpr int val = -1;
  typedef f0_ f0;
  typedef f1_ f1;
  typedef f2_ f2;
};

template <int tag_, typename f0_, typename f1_, typename f2_>
struct Cons_<tag_, f0_, f1_, f2_> {
  static constexpr int tag = tag_;
  static constexpr bool nonzero = true;
  static constexpr int val = -1;
  typedef f0_ f0;
  typedef f1_ f1;
  typedef f2_ f2;
};

template <typename filter, typename p, typename x, typename l, bool cond>
struct ifthenelse;

template <typename filter, typename p, typename x, typename l>
struct ifthenelse<filter, p, x, l, true> {
  typedef Cons<x,
        typename filter::template app<p>::res::template app<l>::res> res;
};

template <typename filter, typename p, typename x, typename l>
struct ifthenelse<filter, p, x, l, false> {
  typedef typename filter::template app<p>::res::template app<l>::res res;
};

template <typename filter, typename p, typename param, bool cond>
struct ifthenelse_;

template <typename filter, typename p, typename param>
struct ifthenelse_<filter, p, param, true> {
  typedef typename param::f1 l;
  typedef typename param::f0 x;
  typedef typename ifthenelse<filter, p, x, l,
        p::template app<x>::res::nonzero>::res res;
};

template <typename filter, typename p, typename param>
struct ifthenelse_<filter, p, param, false> {
  typedef I<0> res;
};

template <typename init, typename i, typename last, typename f, bool cond>
struct ifthenelse_2;

template <typename init, typename i, typename last, typename f>
struct ifthenelse_2<init, i, last, f, true> {
  typedef I<0> res;
};

template <typename init, typename i, typename last, typename f>
struct ifthenelse_2<init, i, last, f, false> {
  typedef Cons<typename f::template app<i>::res,
        typename init::template app<I<(i::val) + (I<1>::val)>>::
            template app<last>::res::template app<f>::res> res;
};

template <typename List, typename sieve, typename candidates, bool cond>
struct ifthenelse_3;

template <typename List, typename sieve, typename candidates>
struct ifthenelse_3<List, sieve, candidates, true> {
  typedef typename candidates::f0 p;
  struct Primes_primes_sieve__fun_ {
    template <typename n>
    struct app { typedef I<(I<(n::val) % (p::val)>::val) != I<0>::val> res; };
  };
  typedef Cons<p,
        typename sieve::template app<
          typename List::f0::template app<Primes_primes_sieve__fun_>::res::
            template app<typename candidates::f1>::res
        >::res> res;
};

template <typename List, typename sieve, typename candidates>
struct ifthenelse_3<List, sieve, candidates, false> {
  typedef I<0> res;
};

struct filter {
  template <typename p>
  struct app {
    struct res {
      template <typename param>
      struct app { typedef typename ifthenelse_<filter, p, param,
          param::nonzero>::res res; };
    };
  };
};

struct init {
  template <typename i>
  struct app {
    struct res {
      template <typename last>
      struct app {
        struct res {
          template <typename f>
          struct app { typedef typename ifthenelse_2<init, i, last, f,
                I<(i::val) > (last::val)>::nonzero>::res res; };
        };
      };
    };
  };
};

typedef Cons<filter, init> List;

struct primes {
  template <typename n>
  struct app {
    struct sieve {
      template <typename candidates>
      struct app { typedef typename ifthenelse_3<List, sieve,
          candidates, candidates::nonzero>::res res; };
    };

    struct Primes_primes__fun_ {
      template <typename i> struct app { typedef i res; };
    };

    typedef typename sieve::template app<
        typename List::f1::template app<I<2>>::res::
          template app<n>::res::
            template app<Primes_primes__fun_>::res
      >::res res;
  };
};

struct main {
  template <typename limit_>
  struct app { typedef typename primes::template app<limit_>::res res; };
};

typedef typename main::template app<I<limit>>::res output;
typedef typename output::print print;

C++ は純粋関数型言語である

C++ は「可変状態」をサポートしません。
そのため、OCaml 標準ライブラリ(多くはミューテーションに依存)を直接利用できず、
上記例では

List
モジュールの一部を純粋関数型スタイルで再実装しています。


生成された C++ プログラムの実行方法

プログラムを実行するには C++ コンパイラ(またはインタプリタ)が必要です。
例では g++ を使用し、

-D
オプションでコンパイル時にパラメータを渡しています。

$ g++ -Dlimit=100 primes.cpp

limit = 100
の場合、下記のように 100 以下の素数が出力されます。
ただし
print
が型として存在しないため、次のようなエラーが発生します。

primes.cpp:159:26: error: ‘print’ in ‘output’
...

この不思議な出力形式は、C++ が C の高度なプリプロセッサとして誕生したことに由来します。
コンパイラのエラーメッセージでは OCaml の infix

::
ではなく、
ネストされた
Cons<hd, tl>
セルが表示されます。

注意
C++ は既に

::
を名前解決演算子として使用しているため、
OCaml のリスト構造を表す
::
構文はサポートされません。
出力を読む際には「ネストされた
Cons<hd, tl>
」とみなしてください。


テンプレート深度の増加

長時間実行する計算では、デフォルトのテンプレート深度を超えてしまうことがあります。
次のようにオプションを追加して深さを増やせます。

$ g++ -ftemplate-depth=999999 -Dlimit=10000 primes.cpp

私の環境では、10 000 までの素数を約半分の時間で出力し、
約 11 GiB のメモリを消費しました。
コンパイラによって性能は異なります:

clang++
は同じタスクを約 1 秒で完了し、
わずか数 MB を使用しますが、極端に大きい入力ではセグメントフォールトすることがあります。


アルゴリズム上の考慮点

今回示した単純なエラトステネスの篩は、素数生成には効率的とは言えません。
O'Neill の優先度付きキューアルゴリズム(Okasaki の左辺ヒープに基づく)は
ずっと高速です。
この手法を用いると、

g++
で 10 000 以下の素数を約 8 秒で計算し、
メモリは約 3.1 GiB に抑えられます。


今後の展望

バックエンドは他言語への拡張も可能です。
Rust が部分テンプレート特殊化をサポートするようになれば、
OCaml プログラムを直接 Rust で実行できるようになるでしょう。

同じ日のほかのニュース

一覧に戻る →

2026/04/02 2:11

NASAの「アーテミス II」クルーが月へ発進します

## Japanese Translation: > **Artemis II 発射成功:** オリオンのソーラーアレイ翼が午後6時59分に完全展開し、各翼は約15,000セルを含み、およそ63フィート(19メートル)にわたります。 > > **主要推進マイルストーン:** 固体ロケットブースターが午後6時37分に分離;SLSコアステージの主エンジンカットオフは午後6時43分に発生;その後、コアステージ分離が午後6時59分に行われ、最初の推進フェーズが終了しました。 > > **打ち上げタイミング:** 打ち上げウィンドウは午後6時24分(EDT)で開き、ロケットコンプレックス-39Bからの離陸は午後6時35分に実施されました。 > > **事前準備:** 最終天気ブリーフィング(約80%が許可)、クルー服チェック、ハッチ閉鎖、打ち上げ中止システム検証をカウントダウン前に完了しました。コアステージとICPSのタンク作業はLH₂/LOX のスロー・フィル→ファスト・フィル→リペンリッシュ段階で行われました。 > > **離陸後の計画マヌーバー:** オリオンは低軌道上昇マヌーバー(PRM)を実施し、その後遠地点上昇バーナー(ARB)で深宇宙軌道を形成します。 > > **クルーと運用:** 乗員の4名は司令官レイド・ウィズマン、パイロットビクター・グローバー、クリスティーナ・コッホ、およびCSA宇宙飛行士ジェレミー・ハンセンです。NASAはケネディ宇宙センターで午後9時に打ち上げ後の記者会見を開催し、その後クルーは中間液体推進ステージを使用した近接操作デモンストレーションの準備に取り組みます。

2026/04/02 6:36

DRAM の価格がホビイスト向けのSBC市場を潰しつつあります。

## Japanese Translation: ### 改訂要約 ホビイスト向けのシングルボードコンピュータ(SBC)市場は、DRAM価格が急騰したため圧迫を受けています。Raspberry Pi は LPDDR4 RAM を搭載したすべての Pi モデルの価格を引き上げ、新しい 3 GB‑RAM の Pi 4 を $83.75 に設定し、16 GB の Pi 5 を $299.99 にしました。これらの値上げは主に LPDDR チップのコスト増によるもので、現在ボードコストの大部分を占めています。その結果、4 GB 以上の RAM を搭載したボードは多くのホビイストにとって手が届かないものとなっています。以前はお得だったミニ PC は 8 GB バリアントで $250 を超え、同様に使用済み PC も 4 GB 超で $250 を上回る価格になっています。Radxa は昨年新しいボードをリリースし続けましたが、ほかのベンダーは発売を減速または停止しています。Raspberry Pi の創設者 Eben Upton は「メモリ価格は現在の非常に高い水準で永続するわけではない」と述べており、その期間は不確実です。著者自身のプロジェクトは学習コストを下げ、破損リスクを減らすために $100 未満の部品を対象としています。DRAM 価格が高止まりする場合、ホビイストは古い SBC やマイクロコントローラへ戻る可能性が高く、手頃な選択肢が狭まり、小規模ベンダーは事業停止のリスクに直面します。Raspberry Pi の強力なマイクロコントローラエコシステムと産業基盤はある程度のレジリエンスを提供しますが、ホビイストセグメント全体での多様性は減少する可能性があります。

2026/04/02 1:14

**エムダッシュ** 「プラグインのセキュリティ問題を解決した、WordPress の精神的後継者」

## Japanese Translation: **改善された概要** EmDashはMITライセンスでリリースされ、GitHubにホストされたオープンソースの全TypeScript CMSです。WordPress の精神的後継者として位置付けられ、サーバーレスまたは Cloudflare の workerd ランタイムを介して任意の Node.js プラットフォーム上で動作します。プラグインは Dynamic Workers を通じてサンドボックス化され、各プラグインがマニフェスト(例:`read:content`、`email:send`)で必要な機能を宣言し、その権限のみを受け取ります。プラットフォームは x402 支払標準に対応しており、サブスクリプションなしで使用料ベースのコンテンツ収益化が可能です。 テーマは Astro プロジェクトとして構築されます—ルート、レイアウト、コンポーネント、スタイル、およびシードファイルを定義し、データベースアクセスをテーマから除外してセキュリティを強化します。EmDash は AI ネイティブ CMS インターフェイスを提供し、会話型インタラクションのための Agent Skills、CLI、および MCP サーバーにより、コンテンツ、メディア、スキーマ、プラグインのプログラム的管理が可能です。 認証はデフォルトでパスキー ベースのログインとロールベース ACL を採用し、SSO は IdP メタデータを介して統合できます。WordPress サイトは WXR エクスポートまたは EmDash Exporter プラグインを使用して移行でき、コンテンツとメディアが自動的に EmDash コレクションへ移動します。本プロジェクトは、プラグインが任意のライセンスを持ち、安全なサンドボックス内で独立して実行できるようにすることで、WordPress のマーケットプレイスロックインを打破することを目指しています。 開発者は `npm create emdash@latest` で v0.1.0 をローカルまたは Cloudflare 上でプレビューできますし、管理インターフェイスは EmDash Playground でライブです。フィードバック・貢献・問い合わせはメール([devs@emdash.org](mailto:devs@emdash.org))や更新情報のオプションメールリスト登録を通じて歓迎されます。

新しいC++バックエンド for `ocamlc` | そっか~ニュース