GHCJSとは|Haskell→JS変換の仕組みとGHC統合後の選び方【2026年版】
GHCJSは、純粋関数型言語であるHaskellのコードをブラウザで動くJavaScriptへ変換するコンパイラです。かつてはHaskellでWebフロントエンドを書くための定番でしたが、現在は本体であるGHCにJavaScriptバックエンドが統合され、立ち位置が大きく変わりました。この記事では、GHCJSの仕組みと、GHC本体のJS/WASMバックエンドとの違い(Template HaskellやFFI構文、コードサイズの差分)、Miso・ReflexといったフロントエンドフレームワークのWASM移行、そして2026年時点で新規・既存・学習それぞれの場面でどれを選ぶべきかまでを、一次情報に基づいて整理します。
目次
- 1 まとめ|新規採用はGHC本体のJS/WASMバックエンドが基本、GHCJSは保守用途に限定
- 2 GHCJSの基本|Haskellコードをブラウザ向けJavaScriptへ変換するコンパイラの役割
- 3 GHCJSの現在地|GHC8.10で開発停止した経緯とGHC本体JSバックエンドへの統合
- 4 GHCJSとGHC本体JSバックエンドの違い|FFI構文・Template Haskell・最適化の差分
- 5 GHCJS導入でつまずく実務ポイント|Nixビルド・大きなメモリ消費・CPPマクロ
- 6 GHCJS上のフロントエンド資産|Miso・Reflex・jsaddleとWASM移行の現実的な道筋
- 7 2026年にGHCJSを選ぶべきか|新規・既存・学習の3場面で異なる判断基準
- 8 よくある質問
- 9 関連記事
まとめ|新規採用はGHC本体のJS/WASMバックエンドが基本、GHCJSは保守用途に限定
結論として、2026年にHaskellをJavaScriptへ変換したいなら、独立プロジェクトのGHCJSではなく、GHC本体に統合されたJavaScriptバックエンド(GHC 9.6.1以降)かWebAssembly(WASM)バックエンドを起点に検討するのが現実的です。GHCJSはGHC 8.10系で開発が止まっており、最新のGHCや言語拡張には追従していません。
一方で、GHCJSがすぐに無価値になるわけではありません。既存のGHCJS製アプリの保守や、Miso・Reflexで書かれた資産の維持には依然として使えます。新規は本体バックエンド、既存は段階的な移行、という役割分担で考えると判断を誤りにくくなります。詳しい根拠と手順は以下で解説します。
GHCJSの基本|Haskellコードをブラウザ向けJavaScriptへ変換するコンパイラの役割
まずGHCJSが「何をするツールなのか」を押さえます。名前はGHC(Glasgow Haskell Compiler)にJavaScriptのJSを加えたもので、Haskellの世界の資産をそのままWeb上で動かすことを目的に作られました。
GHCJSがHaskellプログラムをJavaScriptへ変換する基本的な流れと.jsexe出力
GHCJSはGHCのAPIを利用し、Haskellのソースを解析した中間表現からJavaScriptを生成するコンパイラです。コンパイル結果は、たとえばFoo.hsから実行用スクリプト本体とFoo.jsexeというディレクトリが作られ、生成されたall.jsをNode.jsで実行したり、HTMLのscriptタグに読み込んでブラウザで動かしたりできます。
つまりGHCJSは、Haskellの型システムや遅延評価といった性質を保ったまま、出力先だけをネイティブバイナリからJavaScriptに差し替える仕組みだと捉えると分かりやすくなります。サーバーサイドで書いたロジックをフロントエンドでも共有しやすい点が、当初の大きな魅力でした。
型安全なHaskellでWebフロントを書くというGHCJSが狙った課題
GHCJSが解こうとしたのは、いわゆる「JavaScript問題」です。クライアントサイドの言語が事実上JavaScript一択である一方、Haskellの強力な型検査やコンパイル時のエラー検出をフロントにも持ち込みたい、という需要に応えるものでした。Haskellという純粋関数型言語が持つ副作用の分離や型安全性についてはHaskellの特徴と利用シーンの解説で詳しく触れていますが、GHCJSはその利点をブラウザにまで広げる試みだったといえます。
この発想自体は今も生きており、後継であるGHCのJS/WASMバックエンドや、後述するMiso・Reflexといったフレームワークに引き継がれています。GHCJSは「Haskellでフロントを書く」という方向性の出発点として理解しておくと、現在の選択肢の意味も掴みやすくなります。
GHCJSの現在地|GHC8.10で開発停止した経緯とGHC本体JSバックエンドへの統合
GHCJSを検討するうえで最も重要なのが、この「現在地」です。記事や情報の多くは数年前のもので、当時の前提のまま読むと判断を誤ります。
GHCJSがGHC8.10系で止まりフォーク維持が重荷になった経緯
GHCJSはGHC本体のフォーク(派生版)として作られていたため、GHCが更新されるたびに同じ変更を取り込み直す必要がありました。開発の中心だったLuite Stegeman氏はこのフォーク維持を約10年続けましたが、最終的にGHCJSはGHC 8.10系に留まり、本体から3メジャーバージョン以上遅れた状態になりました。
この「遅れ」は単なるバージョン番号の問題ではなく、新しい言語拡張やライブラリ、ツールチェーンに追従できないことを意味します。フォーク方式の構造的な負担が、GHCJSが行き詰まった根本的な理由でした。
GHCJSのコード生成器がGHC9.6.1のJavaScriptバックエンドへ統合された流れ
そこで採られたのが「フォークを維持し続けるのではなく、GHC本体に取り込む」という方針です。IOG(旧IOHK)のチームが中心となり、GHCJSのコード生成器をベースにしたJavaScriptバックエンドを開発。2022年11月30日にGHCのmasterへマージされ、GHC 9.6.1(2023年3月リリース)で正式に提供が始まりました。同じリリースではWebAssembly向けのバックエンドも同時に導入されています。
これにより、JavaScript向けのコード生成はGHC本体のリリースサイクルに乗ることになり、フォークを別管理する負担は解消されました。GHCJSの技術的な核は、いわば本体に「昇格」した形です。
GHC本体JSバックエンドが現在もテクノロジープレビューである位置づけ
ただし、統合されたから即「完成品」というわけではありません。GHCのユーザーガイドでは、JavaScriptバックエンドは引き続きテクノロジープレビュー(技術プレビュー)として位置づけられ、本格的なプロジェクトや本番環境には適さないと明記されています。標準の配布バイナリ(bindist)には同梱されておらず、利用にはGHCをクロスコンパイラとして手動ビルドする必要があります。
なお、機能面では着実に前進しており、GHC 9.8ではJavaScript側からHaskell関数を呼び出すコールバックに対応しました。最新の安定版はGHC 9.14.1(2025年12月19日)で、9.14からは長期サポート(LTS)リリースの運用も始まっています。「将来性のある本命だが、まだ枯れきってはいない」という温度感で捉えるのが正確です。
GHCJSとGHC本体JSバックエンドの違い|FFI構文・Template Haskell・最適化の差分
「GHCJSと本体のJSバックエンドは結局同じなのか」という疑問は多く見られます。コード生成器の系譜は同じでも、移植の際に意図的に省かれた機能があり、その差分を知らないと移行でつまずきます。
GHCJS・GHC JSバックエンド・WASMバックエンドの対応範囲を整理した比較
まず三者の位置づけを俯瞰します。GHCJSは独立した旧来のフォーク、JSバックエンドはその後継、WASMバックエンドは別アプローチでブラウザ実行を狙うもの、という関係です。
| 項目 | GHCJS(旧フォーク) | GHC JSバックエンド | GHC WASMバックエンド |
|---|---|---|---|
| 形態 | GHC本体のフォーク | GHC本体に統合 | GHC本体に統合 |
| 対応GHC | 8.10系で停止 | 9.6.1以降 | 9.6以降(9.10系で機能拡充) |
| 出力 | JavaScript | JavaScript | WebAssembly |
| Template Haskell | 対応 | 初期リリースで非対応(段階的に整備中) | 対応(ブラウザでの評価も可) |
| GHCi | GHCJSi(旧) | 限定的 | 対応(ブラウザモードあり) |
| 本番利用 | 新規は非推奨 | プレビュー(本番非推奨) | プレビュー(本番非推奨) |
表のとおり、どれも「今すぐ無条件に本番投入できる枯れた選択肢」ではない点が共通します。違いは形態と成熟の方向性にあり、GHCJSだけが更新の止まった旧世代である、という構図です。
初期のJSバックエンドで省かれたTemplate Haskell・最適化・コンパクタの影響
本体への統合時、レビュー可能で十分にテストされたコードを優先するため、いくつかの機能が初期リリースから外されました。代表的なのが、関数単位の最適化(optimizer)、リンク時に重複を取り除き初期化コードを圧縮するコンパクタ(compactor)、インクリメンタルリンカ、そしてTemplate Haskellです。
とりわけ最適化とコンパクタの欠如は、生成されるJavaScriptのコードサイズと起動時間に直結します。GHCJS時代に蓄積された圧縮の工夫が初期版では効かないため、出力が大きくなりやすいという実務上の注意点が生まれます。Template Haskellを使うコードは初期のJSバックエンドではそのままコンパイルできず、移行時に対応状況の確認が欠かせません。
GHCJS独自のFFIプレースホルダ構文「$r」「$1」記法の扱いの違い
GHCJSのFFI(外部関数インターフェース)には、インラインでJavaScriptを書ける独自の拡張構文がありました。引数を$1、$2…、戻り値を$rで表し、たとえばforeign import javascript unsafe "$r = Math.sin($1)" jsSin :: Double -> Doubleのように記述するものです。
このGHCJS拡張のFFIプレースホルダ構文は、設計を整理する目的で本体JSバックエンドの初期版から取り除かれました。そのため、$rや$1を多用したGHCJS向けコードは、移植時にFFI部分の書き換えが必要になります。検索でよく見かける「ドル記号のエスケープ」の話題は、この独自記法に由来するものだと理解しておくと混乱を避けられます。
GHCJS導入でつまずく実務ポイント|Nixビルド・大きなメモリ消費・CPPマクロ
実際にGHCJS(やその周辺フレームワーク)を動かそうとすると、言語そのものより環境構築でつまずくケースが目立ちます。代表的なハマりどころを先に押さえておきます。
GHCJSの環境構築でNixと大きなメモリ消費がハードルになる理由
GHCJSベースの開発環境は、依存関係の再現にNixパッケージマネージャを前提とすることが多く、たとえばReflex向けのReflex PlatformはNixに依存し、Windowsでは動作しません。さらにGHCJSはコンパイル時に多くのメモリを使い、Reflex Platformの公式説明では16GBのメモリが推奨、8GB前後が下限の目安とされています。
加えて、ビルドにはemscriptenのツールチェーンやNode.jsが必要になるなど、前提ツールが多い点も障壁です。生成物のコードサイズが大きくなりやすいことも含め、まずは小さなHello World相当の構成で一通り通してから本題に入るのが安全です。
条件付きコンパイルで使う「__GHCJS__」マクロと「if impl(ghcjs)」の書き方
HaskellのコードをGHCJSとネイティブGHCの両方でビルドしたい場合、コンパイラごとに処理を分けるために条件付きコンパイルを使います。GHCJSでビルドしているかどうかはCPPマクロ__GHCJS__が定義されているかで判定し、ソース内で#ifdef __GHCJS__を使って分岐させます。
パッケージ単位では、cabalファイルでif impl(ghcjs)という条件ブロックを使い、GHCJSのときだけ依存パッケージやコンパイルオプション(cpp-options: -D__GHCJS__など)を切り替えます。FFIまわりやライブラリ差異を吸収する定番の手段で、GHCJS資産を読み解く際にも頻出する記法です。
GHCJS上のフロントエンド資産|Miso・Reflex・jsaddleとWASM移行の現実的な道筋
GHCJSは単体で語られるより、その上に積まれたフレームワークとセットで使われてきました。資産がどこに紐づいているかを把握すると、移行の道筋が見えてきます。
Miso・Reflexなどjsaddleベースのフロントエンド資産の位置づけ
HaskellによるWebフロント開発で代表的なのがMisoとReflexです。MisoはElm風のアーキテクチャと仮想DOMの差分更新を採用した軽量フレームワーク、Reflexは関数型リアクティブプログラミング(FRP)に基づき、reflex-domを通じてブラウザUIを構築します。どちらもjsaddleを基盤としており、歴史的にはGHCJS経由でJavaScriptへコンパイルして使われてきました。
つまり「GHCJSの資産」とは、多くの場合これらフレームワークで書かれたアプリのことを指します。移行を考えるときは、GHCJSそのものより、土台のjsaddleやDOM層がどのバックエンドに対応しているかが鍵になります。
ghcjs-domとjsaddleが担うDOM操作・JavaScript連携の役割
jsaddleはHaskellからJavaScriptを呼び出す抽象レイヤーで、開発時はネイティブGHC上で、本番ではブラウザ上で同じコードを動かせるよう橋渡しします。ghcjs-domは、その上でDOM操作のAPIをHaskellから扱えるようにするライブラリです。
この二層が間に挟まることで、アプリ側のコードはバックエンドの違いを意識せずに書ける設計になっています。逆にいえば、移行時に手当てが必要なのは主にこの層であり、アプリ本体のロジックは比較的そのまま活かせる可能性が高いということです。
jsaddle-wasmでMiso/ReflexをWASMバックエンドへ載せ替える道筋
現実的な移行の選択肢として注目されているのが、jsaddle-wasmです。これはGHCのWASMバックエンド上でjsaddleのアクションを実行するためのもので、MisoやReflexのアプリをブラウザで動かす用途に使えます。WASM向けJSFFIに対応したGHC(9.10以降)が前提で、プロジェクト自体はまだ初期段階と位置づけられています。
DOM層については、現状ghcjs-dom-jsaddleがそのまま動くため、当面は無理に置き換えず様子を見るのが妥当とされています。GHCJSに縛られていたMiso/Reflex資産を、本体に統合されたバックエンドへ寄せていく具体的な経路として押さえておく価値があります。
2026年にGHCJSを選ぶべきか|新規・既存・学習の3場面で異なる判断基準
最後に、立場ごとの判断を整理します。「GHCJSは使うべきか」という問いは、新規開発か、既存保守か、学習目的かで答えが変わります。
新規プロジェクトでGHC本体のJS/WASMバックエンドを選ぶ判断
新しくHaskellでWeb系の成果物を作るなら、GHCJSを起点にする理由はほぼありません。GHC 8.10系で止まっているため最新の言語機能やライブラリに追従できず、長期的な保守も見込みにくいからです。本体に統合されたJSバックエンド、あるいはGHCiやTemplate Haskell、ブラウザ実行に対応しつつあるWASMバックエンドを起点に検討するのが妥当です。
ただし、いずれも公式にはテクノロジープレビュー扱いである点は前提に置く必要があります。要件がシビアな本番向けなら、PureScriptやElmなど成熟した別系統の選択肢を含めて比較するのが現実的な姿勢です。
既存のGHCJSアプリを保守・移行する場合の現実的な進め方
すでにGHCJSで動いているアプリがある場合、慌てて全面書き換えをする必要はありません。動作している資産はそのまま保守しつつ、移行のコストとリスクを段階的に見積もるのが堅実です。具体的には、Template HaskellやGHCJS独自のFFIプレースホルダ構文($r記法)への依存度を棚卸しし、書き換え量を把握することから始めます。
フレームワークを使っている場合は、jsaddle-wasmを通じたWASMバックエンドへの載せ替えが移行先の候補になります。土台のjsaddle・DOM層に変更が集中するため、そこを小さく検証してから本体ロジックを移すと安全です。
学習・検証目的でGHCJSの資料に触れるときの注意点
学習目的でGHCJSの記事やチュートリアルを読む際は、その情報が「いつ時点のものか」を必ず確認してください。2021年前後までの解説はGHCJSを前提に書かれており、現在の本体統合という文脈が抜けていることが多いためです。
仕組みの理解教材としてGHCJSのドキュメントを読むのは有益ですが、そこで得たコマンドや構成をそのまま新規環境に適用すると、停止したバージョンに引きずられます。概念はGHCJSで学び、手を動かす実装は本体バックエンドで試す、という使い分けがおすすめです。
よくある質問
GHCJSについて検索で多く見られる疑問を、現在の状況に沿って簡潔に回答します。
GHCJSとGHCのJavaScriptバックエンドは何が違いますか?
GHCJSはGHC本体のフォーク(独立した派生版)で、現在はGHC 8.10系で開発が止まっています。一方、GHCのJavaScriptバックエンドは、そのGHCJSのコード生成器をベースに本体へ統合したもので、GHC 9.6.1以降で利用できます。系譜は同じですが、本体バックエンドは初期にTemplate Haskellや独自FFI構文などを省いており、機能差分があります。
GHCJSはまだメンテナンスされていますか?最新のGHCで使えますか?
GHCJS本体はGHC 8.10系に留まっており、最新のGHC(9.14系)には対応していません。既存資産の保守には使えますが、新規開発で最新のGHCと組み合わせる用途には向きません。最新環境でHaskellをJavaScript化したい場合は、本体のJS/WASMバックエンドを検討してください。
GHCJSとPureScriptはどちらを選ぶべきですか?
既存のHaskell資産をそのままWebに持ち込みたいならGHCJS系の流れ、フロントエンド専用に手軽な環境を求めるならPureScriptが向きます。PureScriptはnpmから導入でき、spagoという専用のパッケージマネージャを持つなど立ち上げが容易です。新規でHaskellにこだわらないなら、PureScriptは有力な比較対象になります。
GHCJSのFFIで見かける「$1」や「$r」とは何ですか?
GHCJS独自のFFIプレースホルダ構文で、$1や$2が関数の引数、$rが戻り値を表します。たとえば"$r = $1 + $2"のようにインラインでJavaScriptを書けます。この拡張構文は本体JSバックエンドの初期版では取り除かれているため、移植時には該当箇所の書き換えが必要です。
MisoやReflexで作ったアプリは新しいバックエンドへ移行できますか?
移行の道筋は用意されつつあります。MisoとReflexはjsaddleを基盤としており、jsaddle-wasm(GHC 9.10以降が前提)を使えばWASMバックエンド上でブラウザ実行できます。ただし同プロジェクトは初期段階のため、まずは小規模な構成で検証してから本格移行を判断するのが安全です。
関連記事
- Haskellとは?特徴と利用シーンを徹底解説:GHCJSの前提となるHaskell言語そのものの特徴(純粋関数型・型システム・遅延評価)を知りたい方向けの解説です。