アラフラで採用する予定のjhc、おおざっぱな使い方はわかっているのでゲソが細かいところは知らんでゲソ。 気になるのはGHCとの違いでゲソが、内部解析のための足掛かりになるかもしれないので、使い方はしっかりマスターしておきたいでゲッソ!
しかし前々回の記事で紹介した Jhc User’s Manual は当然のごとく英語…自然言語さえ苦手なワシには読めんでゲソ。 そこで根性で日本語訳してみたでゲッソ! 1 ざんねんながらワシには専門知識が圧倒的に欠如しているので、用語のミスや根本的な間違いなどを見つけたら是非教えて欲しいでゲソ。 この記事は翻訳の修正をするたびに随時更新するつもりでゲソ。
この翻訳には po4a を使っていて、gettextの翻訳データは po/ja.po - jhc-arafura in Metasepi - Gitorious にあるので、po4aとgitを使える人はpull requestを送ってくれるのも歓迎でゲッソ!
2013/03/16追記: Metasepi向けのjhcの開発は Ajhc で行なうことになったでゲソ。 Ajhcで追加された機能は Ajhc User’s Manual (日本語訳) を参照のこと、でゲッソ!
Jhcはソースコードの依存関係を独自に解析してくれるため、 コマンドラインから’main’関数を含んだファイルを指定するだけでソースコード全体をコンパイルできる。 例えばソースコード’HelloWorld.hs’をコンパイルしたければ、次のようにコンパイルすれば’hello’という名前の実行バイナリが生成される。
; jhc HelloWorld.hs -o hello
Jhcはサーチパスからモジュールを探す。 そのサーチパスは既定値ではカレントディレクトリである。 モジュールは名前にもとづいて検索される。 例えばData.Fooモジュールは’Data/Foo.hs’もしくは’Data.Foo.hs’のパスから探されることになる。 このサーチパスは’-i’コマンドラインオプションを使って指定できる。 また、’JHC_PATH’環境変数を使って指定することもできる。
jhcライブラリは、例えば’base-1.0.hl’のような、’hl’という拡張子を持つ単一のファイルである。 何かHaskellライブラリを使うためには、jhcの検索するディレクトリにこのファイルを置くだけで良い。 例えば $HOME/lib/jhc などだ。 JHC_LIBRARY_PATH環境変数を定義することで、別の位置をライブラリのサーチパスとして指定することができる。 また、-Lコマンドラインオプションを使ってもサーチパスを指定できる。 さらに-L-オプションを使えばサーチパスの設定を既定値に戻すことさえできる。
‘hl’ファイルが適切な位置に置かれていれば、’-p’コマンドラインオプションを使って使用するライブラリを指定することができる。 例えば’mylibrary-1.0.hl’を既にサーチパスに配置済みであれば、以下のように使うことができる。
; jhc -p mylibrary MyProgram.hs -o myprogram
–list-librariesオプションを使えば全ての使用可能なライブラリのリストを得られる。 さらに’-v’オプションを追加することで、各ライブラリに関する詳細な情報を得ることもできる。 これらの情報はYAMLフォーマットを使って生成されたものである。
Jhcの挙動はいくつかの環境変数によって制御される。
JHC_OPTS : jhcのコマンドライン直後に挿入されるオプション群
JHC_PATH : モジュールの検索パス
JHC_LIBRARY_PATH : ライブラリの検索パス
JHC_CACHE : jhcがキャッシュとして使用するディレクトリ。キャッシュはjhcのパフォーマンス向上にはかかせない。既定値は~/.jhc/cache。
ライブラリは–build-hlオプションで指定されたライブラリファイルの内容にもとづいてビルドされる。 このライブラリファイルのフォーマットはYAMLである。
; jhc --build-hl mylibrary.yaml
ライブラリファイルはYAMLフォーマットで、jhcは自分の理解できるフィールドだけを解釈し、残りは無視する。
Name : ライブラリの名前
Version : ライブラリのバージョン。バージョン番号は’-p’コマンドラインオプションを使う際に区別するために使われる。しかし、それ以外の用途にはjhcは使わない。
Exposed-Modules : ライブラリに含まれ、ライブラリの利用者への公開インターフェイスとなる一連のモジュール。この欄が他のライブラリ内のモジュールを含んでいた場合、再度このライブラリでも公開インターフェイス扱いされることになる。
Hidden-Modules : ライブラリ内部で使用されるかもしれないが、公開インターフェイスにはならない一連のモジュール。jhcはこの情報を元にして最適化を行なう。このリストがライブラリのビルドに網羅的でなければ、jhcはワーニングメッセージを出力する。
Extensions : このモジュールをコンパイルするのに必要な拡張のリスト。jhcはできうるかぎりghc拡張と似た拡張を提供するつもりである。
Options : ライブラリビルド時に使うその他のコマンドラインオプション
Build-Depends : ビルドに必要になるライブラリ。’-p’コマンドラインオプションと同じフォーマットで指定する。
Hs-Source-Dirs : Haskellソースコードを探すディレクトリ。‘-i’コマンドラインオプションと異なり、この欄にはライブラリ詳細を記載した.yamlファイルからの相対パスを記載する。’-i’オプションを使う際にはカレントワーキングディレクトリからの相対パスを記載すること。
Include-Dirs : プリプロセッサが’-I’オプションで検索するディレクトリ。このディレクトリもyamlファイルが置かれたディレクトリからの相対パスとして指定すること。
C-Sources : このライブラリを使うプログラムにリンクされるべきC言語ソースコードファイル群。
Include-Sources : このライブラリを使ったプログラムのビルドに必要なC言語のヘッダを指定する。これらのヘッダはビルド時にincludeされることはあっても、実行ファイルにリンクされることはない。
ライブラリファイルの例としてlib/jhc/jhc.yamlとlib/base/base.yamlを参考のこと。
jhcは依存情報を出力できる。 この依存情報はソースコードとライブラリがコンパイル時にその他の要素にどのように依存しているかを表現している。 依存情報は–deps name.yamlオプションをjhcに入力するとname.yamlファイルに出力される。 この依存情報もまたYAMLフォーマットで、各項目な以下のような意味を持っている。
deps.yamlを扱うサンプルとして’utils/deps_to_make.prl’を参照のこと。
Usage: jhc [OPTION...] Main.hs
-V --version バージョン表示
--version-context バージョン履歴表示
--help ヘルプメッセージ表示
--info コンパイラ設定表示
--purge-cache コンパイルキャッッシュの削除
-v --verbose stderrに冗長なメッセージ表示
-z 冗長な統計情報を表示
-d [no-]flag コンパイルパイプラインの特定の情報をダンプ
-f [no-]flag コンパイルフラグの有効/無効
-X ExtensionName 言語拡張の有効化
-o FILE --output=FILE 出力ファイル名を指定
-i DIR --include=DIR ソースファイル検索パス
-I DIR プリプロセッサのインクルードパスを追加
-D NAME=VALUE プリプロセッサに渡すdefineを追加
--optc=option Cコンパイラに渡すその他のオプション
-c モジュールを単体でコンパイル
-C C言語コードへコンパイル
-E ソースコードをプリプロセッサにかけてstdoutへ出力
-k --keepgoing エラーを無視してコンパイルを続行
--cross クロスコンパイルを有効に、-mオプションでターゲットを指定すること
--stop=parse/typecheck/c parse/typecheck/cの直後で停止する
--width=COLUMNS デバッグ出力のスクリーン幅を変更
--main=Main.main mainエントリポイントを指定
-m arch --arch=arch クロスコンパイルターゲットを指定
--entry=<expr> mainエントリポイントを式を使って指定
--show-ho=file.ho hoファイルの概要をダンプ
--noauto haskell98パッケージを自動的に読み込まない
-p package 指定したライブラリを使用する
-L path ライブラリを指定したパスから検索する
--build-hl=desc.yaml 指定したライブラリファイルからHaskellライブラリをビルド
--annotate-source=<dir> 指定したディレクトリにプリプロセス済みソースコードを出力
--deps=<file.yaml> 依存情報を指定したファイルに書き込む
--interactive インタラクティブ実行 (デバッグ用途)
--ignore-cache コンパイルキャッシュを無視する
--readonly-cache コンパイルキャッシュに追加情報を書き込まない
--no-cache コンパイルキャッシュを使わず、書き込みも行なわない
--cache-dir=JHC_CACHE 指定したディレクトリをコンパイルキャッシュとして使う
--stale=Module 指定したモジュールがコンパイルキャッシュにあっても古い情報として扱う
--list-libraries インストール済みライブラリを表示
--tdir=dir/ 中間生成物を出力するディレクトリを指定する
-dオプションの引数: '-d help'オプションで詳細
all-types, aspats, atom, bindgroups, boxy-steps, c, class, class-summary, core, core-afterlift
core-beforelift, core-initial, core-mangled, core-mini, core-pass, core-steps, datatable
datatable-builtin, dcons, decls, defs, derived, e-alias, e-info, e-size, e-verbose, exports, grin
grin-datalog, grin-final, grin-graph, grin-initial, grin-normalized, grin-posteval, grin-preeval
imports, ini, instance, kind, kind-steps, optimization-stats, parsed, preprocessed, program
progress, renamed, rules, rules-spec, scc-modules, sigenv, srcsigs, stats, steps, tags, the
types, verbose, veryverbose
-fオプションの引数: '-f help'オプションで詳細
bang-patterns, boehm, controlled, cpp, debug, default, defaulting, exists, ffi, forall, full-int
glasgow-exts, global-optimize, inline-pragmas, jgc, lint, m4, monomorphism-restriction, negate
prelude, profile, raw, rules, standalone, type-analysis, type-families, unboxed-tuples
unboxed-values, user-kinds, wrapper
jhcの解釈やコンパイルは’-f’フラグでコントロールできる。 これらのオプションを以下に列挙する。また、その効力は’no-’をフラグ名に付けることで無効化できる。
コードオプション | |
---|---|
bang-patterns | - バン!パターン(強制正格評価パターン) |
cpp | HaskellコードをCプリプロセッサにかけてからコンパイル |
exists | - existsキーワードを存在型の解釈に使う |
ffi | 他言語関数インタフェース(FFI)を使う |
forall | - forallキーワードをランクN多相と明白な量化に用いる |
m4 | Haskellコードをm4プリプロセッサにかけてからコンパイル |
prelude | Preludeを暗黙の内にimportする |
type-families | Type Families(型族)を使う |
unboxed-tuples | アンボックス化タプル文法を許容する |
unboxed-values | アンボックス化値文法を許容する |
user-kinds | ユーザ定義の種を使う |
型検査 | |
---|---|
defaulting | 型のデフォルト化を行なう |
monomorphism-restriction | 単相性制限を強制する |
デバッグ | |
---|---|
lint | より多くの型検査を行なう |
最適化オプション | |
---|---|
global-optimize | Eによってプログラム全体を最適化する |
inline-pragmas | inlineプラグマを使う |
rules | rulesプラグマを使う |
type-analysis | メソッド生成直後に型に対する基本的なpoints-to analysisを適用する |
コード生成 | |
---|---|
boehm | Boehm GCを使う |
debug | 実行バイナリ中のデバッグコードを有効に |
full-int | 32bitマシンでIntとWordを32bitに拡張する |
jgc | jgcガーベッジコレクタを使う |
profile | 実行バイナリ中のプロファイルコードを有効に |
raw | main関数をWHNFとして評価する |
standalone | 単独実行可能にコンパイル |
wrapper | main関数を例外ハンドラで包む |
設定既定値 | |
---|---|
default | inline-pragmas rules wrapper defaulting type-analysis monomorphism-restriction global-optimize full-int prelude |
glasgow-exts | forall ffi unboxed-tuples |
‘-d’フラグ付きでjhcを起動することで各種パラメータを出力できる。 次に挙げるのは’-d’フラグに渡すことができるパラメータのリストである。
フロントエンド | |
---|---|
defs | モジュール内で定義された名前を列挙する(?) |
derived | 自動導出されたインスタンスを表示する |
exports | モジュールからエクスポートされている名前を列挙する |
imports | モジュールがインポートしている名前を列挙する |
ini | iniファイルのオプションを表示 |
parsed | パース済みコードを出力 |
preprocessed | プリプロセス後のコードを出力 |
renamed | renaming後のコードを出力 |
scc-modules | 強結合したモジュールを依存度順に表示 |
型検査 | |
---|---|
all-types | 型検査完了後に型テーブルを表示 |
aspats | アズパターンを表示 |
bindgroups | 束縛グループを表示 |
boxy-steps | 型推論の挙動をステップバイステップで表示 |
class | 個々の型クラスの詳細情報を表示 |
class-summary | 型クラス群の概要を表示 |
dcons | データコンストラクタを表示 |
decls | 処理済みの宣言を表示 |
instance | インスタンスを表示 |
kind | モジュールへの種推論の結果を表示 |
kind-steps | 種推論のステップを表示 |
program | プログラム全体の構造 |
sigenv | 型シグニチャ初期状態の表示 |
srcsigs | renaming後の型シグニチャを表示 |
types | 定義された全ての名前を含んでいる型テーブルを表示 |
中間コード | |
---|---|
core | core言語コードを表示 |
core-afterlift | hoファイルに書き込む直前のcoreコードを表示 |
core-beforelift | lambda lifting直前のcoreコードを表示 |
core-initial | E.FromHs転換直前のcoreコードを表示 |
core-mangled | grinにかける前の型なしcoreコードを表示 |
core-mini | 個々の関数の最適化の詳細を表示 |
core-pass | コード変形の経過を表示 |
core-steps | コンパイルパスそれぞれのステップについて表示 |
datatable | コンストラクタのデータテーブルを表示 |
datatable-builtin | ビルドイン型のデータテーブルを表示 |
e-alias | 展開済みエイリアスを表示 |
e-info | 束縛された値のタグ情報を表示 |
e-size | コンパイルパスそれぞれでのEのサイズを表示 |
e-verbose | Eコードに冗長なメッセージを表示させる |
optimization-stats | 最適化パスのスタティクスを表示 |
rules | ユーザ定義の書き換え規則を表示する |
rules-spec | 特殊化書き換え規則を表示する |
Grinコード | |
---|---|
grin | grinコードを表示する |
grin-datalog | データベースに適したフォーマットでgrinの情報を印字する |
grin-final | C言語への変換直前のgrinコードを表示 |
grin-graph | 最終的なgrinコードをdotファイルをoutputname_grin.dotに出力する |
grin-initial | core言語から変換された直後のgrinコード |
grin-normalized | 最初の正規化を行なった直後のgrinコード |
grin-posteval | eval/applyインライン化直後のgrinコード |
grin-preeval | eval/applyインライン化直前のgrinコード |
steps | インタープリタの実行を表示 |
tags | タグと型を列挙する |
バックエンドコード | |
---|---|
c | コンパイル時に生成したC言語ソースファイルを削除しない |
内部 | |
---|---|
atom | 終了時にatomテーブルをダンプする |
一般 | |
---|---|
progress | 通常のプログレス表示を行なう |
stats | その他の情報を表示する |
verbose | 冗長なプログレス表示 |
veryverbose | スタティクスを含む冗長なプログレス表示 |
プラグマは特別なコンパイラへの指示で、コンパイラの挙動に特定の影響を与える。 一般的には、コンパイラは自由に独自のプラグを定義できる。 しかしjhcでは他のコンパイラでも使われているものを実装しようと努めている。 プラグマはソースコード中で {-# PRAGMANAME … #-} のような形式で使われる。
これらのプラグマは関数定義のあるファイルと同じファイル中に記述すべき。 インスタンスやクラスの関数に適用するためには、 これらのプラグマをインスタンスやクラスの宣言の節に配置しなければならない。
プラグマ | |
---|---|
NOINLINE | core変形中のインライン化を抑制する。当該関数はgrin変形中にインライン化される かもしれない 。 |
INLINE | 可能であれば関数をインライン化する |
SUPERINLINE | 関数本体を単に複製することになったとしても、とにかくインライン化を行なう |
VCONSTRUCTOR | 関数を仮想コンストラクタとして扱う。CPR解析とworker/wrapper変換は関数をコンストラクタとして扱うことがある。これは’NOINLINE’で実装される。 |
プラグマ | |
---|---|
NOETA | デフォルトではjhcは最適化のために全てのクラスメソッドをイータ変換する。このプラグマはこの挙動を無効化する |
プラグマ | |
---|---|
RULES | 書き換え規則。GHCの書き換え規則と同様の構文で同様の振舞いをする。ただし、’phase’を使うことはできない。 |
CATALYST | 特殊な書き換え規則で、他のRULESの使用中でのみ発火する。そのためCATALYSTは最良でない中間ステージを要求する最適化を許す。 |
SPECIALIZE | 指定された型で特殊化された関数を生成する |
SUPERSPECIALIZE | SPECIALIZEと同じ効力がある。しかし一般化された関数にて特殊化された呼び出しかどうかランタイムでの検査を行なう。 |
プラグマ | |
---|---|
CTYPE | FFIでdataもしくはnewtypeを使うたえに、外部の型を指定する。 |
この型はnewtypeかforeign宣言された型の単項コンストラクタでなければならない。 | |
例えば | |
~~~ | |
data {-# CTYPE “unsigned short” #-} CUShort = CUShort Word16 | |
~~~ |
これらのプラグマはファイルの’先頭’でのみ有効である。 つまりこれらは最初の’module’宣言の前に置かれなければならず、 ファイルの先頭4096バイト以内に配置されねばらなず、 そしてASCII文字セットのみしか使ってはならない。
OPTIONS_JHC : その他のオプションを指定する。これらのオプションはコマンドラインオプションとしても利用できるが、単一のファイルにのみ影響を与えたい場合に重宝する。
LANGUAGE : 言語拡張のオプションを指定する
他のHaskellコンパイラと同じように、jhcでのモジュールはその名前にもとづいて検索される。 ’Data.Foo’モジュールを検索するとjhcは’Data/Foo.hs’ファイルも検索するが、 ’Data.Foo.hs’も同じく検索対象になる。
foreign C importは複数の値を返すことがある。 このような場合、アンボックス化タプルを返値として使う。 最初の返値は関数から直接返された値であり、 残りは関数の引数リストの終端へのポインタが返ってくる。 純粋な(IOではない)関数でのみ複数の値を返値として使うことができる。
-- frexpはC言語のプロトタイプ宣言として
-- double frexp(double x, int *exp);
-- を持つ。そのため、IOとして通常のimportをすることができ、
-- 他の純粋な関数へ渡すにはStorableを使うことになる。
foreign import ccall "math.h frexp" c_frexp :: Double -> Ptr CInt -> IO
Double
-- この拡張を使うと以下のように宣言できる。
foreign import ccall "math.h frexp" c_frexp2 :: Double -> (# Double, CInt #)
-- 二番目の返値は最後の'exp'パラメータによって追加されて、アロケートされた
-- メモリから読み出される。関数に渡されるメモリの内容は未定義である。
呼び出し規約’capi’は’ccall’を使ってたimportのかわりに使われる。 この規約を使うことで、foreignする対象はアドレス空間に実体がなくても良い。 マクロやビルドイン関数もしくはその他コンパイラ独自の機能であっても良い。 jhcはルーチンが決してポインタとしては使用されないこと、依存するヘッダをインクルードすること保証する。 これは’ccall’と異なる。ccallはヘッダがスコープに入っているか、正しい名前のリンカのためのシンボルがエクスポートされているか、なんの保証もしない。
p:foo.c や p:foo.h と書かれたforeign importはそのファイルがパッケージ内部実装の一部として解釈されることを意味している。 jhcはそれらのファイルが同じ名前を持っていても他のパッケージと衝突しないことを保証する。 それらのファイルはライブラリファイルのc-filesとc-headersの項目に列挙されるべきである。
jhcはimportやexportする際の名前空間を持つ。 これらはimport/export宣言によって制限されたり修正されたりする。
* 'type' - 名前が型シノニムであり、'type'や'newtype','data',種宣言のコンストラクタで定義されている
* 'class' - 名前がクラスであることを指定
* 'data' - 名前がデータコンストラクタであることを指定
* 'kind' - 名前がユーザ定義の種であることを指定
型クラスと型シノニムは独立した名前空間にあるので、 同じ名前の型シノニムと型クラスが同じスコープに存在しても問題ない。
-fuser-kindsオプションが有効な場合、jhcはユーザ定義の種を使用可能にする。 その文法は:
data kind Nat = Z | S Nat
ここでは新しい種’Nat’と、その種に存在する2つの型’Zと’S’を定義している。 これらの型は値を持たないアンボックス化された値として表現された種の宣言によって定義されている。 そのためこれらはランタイムでの表現を持たない。(当然、⊥でさえない)
jhcはある環境下にて独立derivingをサポートする。
jhcは高ランク多相性をサポートする。 文脈が高ランク多相型を明確に指定しても、jhcは高ランクの型推論を行なわない。 例えば、型注釈と多相として定義されたデータコンストラクタへの引数を与えれば、正しい多相型が手に入る。
jhcはファーストスラスの存在型をサポートする。 それは’exists’キーワードで使用できる。 また存在データ型はghcと似た方法でサポートしている。
jhcでのアンボックス化された値はGHCと同様に指定できる。 ただし、# をその識別子としては使用しない。 各種アンボックス化されたコンストラクタのための構文として # はまだ使われているが、 通常のHaskellのルールをがHaskellの識別子に使われる。 コンベンションとして’_’の接尾辞の付いた型はアンボックス化された状態にあることを示している。 アンボックス化タプル以外の全てのアンボックス化された値は、-funboxed-valueフラグによって有効になる。 GHCとの互換性のため、MagicHash拡張でもアンボックス化型を有効にできる。
jhcはGHCと同じ文法でアンボックス化タプルをサポートする。(# 2, 4 #) は2つの数値を持つアンボックス化タプルである。 アンボックス化タプルは-funboxed-tuplesオプションによって有効にできる。 アンボックス化タプルは種多相であり、ボックス化とアンボックス化の両方おん値を保持することができる。 (しかし他のアンボックス化タプルを保持することはできない)
アンボックス化文字列は-funboxed-valuesフラグによって有効になる。 通常の文字列と異なり、その名前は’#’で終わる。 アンボックス化文字列は’BitsPtr_’型である。
アンボックス化文字は通常の文字リテラルの後に # を付けて表現する。 アンボックス化文字はChar_型であり、さらにJhc.Prim.Bitsに定義のあるBits32_のnewtypeでもある。
アンボックス化数値は-funboxed-valuesフラグによって有効になる。 それらは 3# や 4# のように’#’を付けて表現する。 もし型が環境によって完全に指定されてて、かつそれが適切なアンボックス化数値型であるなら、jhcは型推論を限定的にサポートする。 そうでない場合にはInt__をデフォルトとして使用する。 型が完全に指定されているかどうかにかかわらず、ランクN型として同じルールが適用される。 アンボックス化数値は列挙可能である。 そのため 0# はアンボックス化されたFalse値として使うことができる。 そして型推論も行なわれる。
アンボックス化値を取り扱うために、適切なプリミティブの演算子をスコープ内に用意する必要がある。 プリミティブのimportのために特殊なFFI宣言を使うことができる。 全てのC–プリミティブはユーティリティ関数と同様にimportできる。 プリミティブimportはのしくみは’スマート’で、newtype宣言を通りぬけ、ボックス化/アンボックス化値が必要か面倒をみてくれる。 だからあなたがプリミティブをCharにimportしたなら、 それが’Char’コンストラクタをボックス化されていると認識し、 Char_がBits32_のnewtypeであることも認識してくれる。 そして遂には正しいBits32_プリミティブを選択してくれるのだ。 importされたプリミティブは通常のHaskell宣言である。 だからモジュールからexport/importできるし、通常の高階関数としても扱える。
FFIの仕様であるforeign importに加えてjhcは’primitive’ importをサポートしていて、 あなたはこれを用いてコンパイラと直接通信することができる。 一般的にこれらは基盤ライブラリの実装以外の用途に使われるべきではない。 一般にこれらは少ないエラーチェックしか行なわない。 jhc内では、すべてのHaskellのエントリはforeign宣言によって形づくられている。
それらは以下の形を取る。
foreign import primitive "specification" haskell_name :: type
“specification”には以下を選択できる。
seq : 最初の引数をWHNFとして評価し、二番目の引数を返す
zero,one : 0か1の値をプリミティブ型に使う
const.C_CONSTANT : constに続くテキストをコンパイル結果のC言語ソースコードに直接埋め込む
peek.TYPE : TYPE型の値の読み込みプリミティブ
poke.TYPE : TYPE型の値の書き込みプリミティブ
error.MESSAGE : results in an error with constant message MESSAGE.
constPeekByte : 指定したバイト数、定数値を読み込み、Jhc.Stringとして使う
box : アンボックス化値を取り、それをボックス化する。ボックス化された形はimportされた型によって決まる
unbox : ボックス化値を取り、アンボックス化する。ボックスの形はimportされた型によいって決まる
increment, decrement : 整数のプリミティブ値を増減させる
fincrement, fdecrement : 浮動小数点のプリミティブ値を増減させる
exitFailure__ : プログラムを即時abortさせる
C– Primitive : C–プリミティブはこの作法でimportする
データタイプ内のクラス文脈は単に無視される。
クラスメソッドはイータ変換されて引数の数は型によって指定される。 部分適用することがめったにないインスタンスではこれはしばしば有益だ。 この挙動はNOETAプラグマによって無効にできる。
GHCのbaseにおおざっぱに沿ったbaseライブラリに加えて、 jhcはいくつかの拡張とマイナーな修正を標準ライブラリに施している。 これらは大部分は後方互換性とクラスシステムのためにデザインされている。
jhcはその他多くの追加ライブラリを提供している。 ここではHaskell98もしくはFFIの仕様で定義されているモジュールに影響する変更だけを挙げるにとどめる。
Data.IntとData.Wordは左記を提供するWordPtr, WordMax, IntPtr, IntMax。 これらはそれぞれC言語のuintptr_t, uintmax_t, intptr_t, intmax_t型に該当する。
IntegerとRationalクラスの実装近くのにfromInt,toInt,fromDouble,toDouble が追加された。
浮動小数点の切り捨てと丸める関数に相違がある。 それらは整数型を返さず、引数と同じ型をかわりに返す。 これらの関数名には’f’という接尾辞が付いている。
jhcはHaskell 98には準拠しているがGHCとの差異がある。
Intは30ビットしか保持せず、切り捨ては検知できない。 もし数値にビット幅とバイナリの意味論が必要な場合、Data.IntかData.Wordの型を使用すること。 IntやWordがあふれた場合の結果は未定義である。
文字はUnicode幅の値しか保持できない。 そのため0x10FFFFより多きな保存されている値は未定義な結果をもたらす。
例え64ビットアーキティクチャ上でもIntとWord型は最大でも32ビット幅である。
テキストベースのIOは現在のロケールで実行される。 つまりUnicodeについてはうまく使用できるが、 IOが8ビット文字を仮定する古いプログラムは異常なふるまいになる。 もしバイナリのIOが必要であれば、バイナリのルーチンを明示的に使うこと。
これらの仕様の欠陥は将来修正される予定である。
Integer型は任意精度型ではなく、IntMax型と同じ精度である。 適切な任意精度ライブラリが見つかれば、この問題は修正されるだろう。
Ix型は自動導出できない。
他の多くのコンパイラと異なり、jhcは生来のクロスコンパイラである。 これが意味することは、jhcによるコンパイルによって、どんなターゲットシステムのためのコードも生成できるということである。 これにより、jhcを利用したクロスコンパイルが非常に簡単になる。 基本的には、クロスコンパイルを行うためにはjhcに ‘–cross’ フラグと、 ターゲットマシンを指定するオプション ’-m’を渡せば良い。 以下がその例である。
; jhc --cross -mwin32 test/HelloWorld.hs
targets.iniファイルに以下のような表記を追加すれば、ターゲットのリストを拡張できる。
このファイルは利用可能なターゲットを列挙する。 フォーマットは以下のエントリから成り立っている。
[targetname]
key1=value
key2=value
key3+=value
merge=targetname2
mergeは特別なキーで他のターゲットから現在のターゲットへ内容をマージしていることを意味する。 設定ファイルは先頭から順に読み込まれ、 与えられたキーに対応する最後の値が実際に使われる。
次に示すのはWindowsクロスコンパイルの例である:
[win32]
cc=i386-mingw32-gcc
cflags+=-mwindows -mno-cygwin
executable_extension=.exe
merge=i686
上記は一般のi686設定を使って少々の他のオプションをコンパイラに設定している。 特別なターゲットである[default]は常に他のターゲットより先に読み込まれる。 もしコマンドラインから’–cross’が指定されたら暗黙にこの設定を読み込む。 さもなければjhcは現アーキティクチャをコンパイル対象としてみなして、 defaultに追加して適切なターゲットを選択する。
jhcは複数のtargets.iniファイルを順に読み込もう試みる。 それらのファイルは以下である。
$PREFIX/etc/jhc-$VERSION/targets.ini : このtargets.iniファイルはjhcによって読み込まれ、既定のオプションを含む。
$PREFIX/etc/jhc-$VERSION/targets-local.ini : jhcは存在すればこのファイルを読み込む。このファイルはローカルのコンパイラのようなシステムグローバルのカスタム設定を含む。
$HOME/.jhc/targets.ini : ユーザローカルの設定
$HOME/etc/jhc/targets.ini : このファイルは隠し属性のディレクトリを嫌う人向けのものである
オプションについて有効とみなされるのは最後の値である。 ユーザ定義のローカル設定はシステムグローバルのローカル設定を上書きする。 またシステムグローバルのローカル設定は組み込みのオプションを上書きする。
オプション | 意味 |
---|---|
cc | Cコンパイラを指定。一般に手元でのコンパイルにはgccを指定し、クロスコンパイルにはARCH−HOST-gccを指定する。 |
byteorder | リトル/ビッグエンディアンのために le もしくは be を指定する |
gc | GCを使う。static もしくは boehm を指定する。 |
cflags | Cコンパイラに渡すオプション |
cflags_debug | デバッグが有効な場合のにみCコンパイラに渡すオプション |
cflags_nodebug | デバッグが無効な場合のにみCコンパイラに渡すオプション |
profile | プロファイルコードを実行バイナリに出力する |
autoload | 自動読み込みされるHaskellライブラリを指定。コンマで区切る。 |
executable_extension | 実行バイナリファイルの拡張子を指定する (すなわちWindowsでは.EXEとなる) |
merge | 他の設定ターゲットの内容を現ターゲットにマージする特別なオプション |
bits | このアーキティクチャでポインタが保有するビット数 |
bits_max | 整数型の最大値が保有するビット数。これは’intmax_t’のC言語型と同じビット数でなければならない。 |
arch | gccに渡すアーキティクチャ名 |
jhcは強いミニマリストであり、あらかじめコンパイルされたランタイムを持たない。 ランタイムはコンパイル時に必要になった際に生成される。 けれどもバックエンドは、GCの選択のように、ランタイム特定のデータ表現を持つ。 C言語ベースのバックエンドのための一般レイアウトを次に示す。 しかしGC種別のようなコンパイラオプションやプログラム全体解析は、どの機能が使われて、最適化されたレイアウトが使用できるかに影響を及ぼす。
アンボックス化値はターゲットの言語の値に直接翻訳される。 アンボックス化Intは引数として直接’int’に翻訳される。 またアンボックス化ポインタは生のポインタになる。 アンボックス化値は特別な解釈を持たず、 GCは面倒をみてくれない 。 もしターゲットの言語が複数返値のような機能をサポートしていない場合、シミュレーションする必要がある。 grinコードが複数返値が補強されたC–もしくはCへの同型としてのみアンボックス化値を取り扱うのは悪い考えではない。
ボックス化された値は標準の表現を持っている。 他の実装と異なり、ボックス化されることがオブジェクトがヒープに配置されることを意味しない。 それはスタックやヒープに確保されるかもしれない。 さらにはスマートポインタの中に組み込まれているかもしれない。 ボックス化されていることはオブジェクトがスマートポインタによって表現されているか、 実際には伝統的なポインタであるかもしれないのだ。
jhcにおけるボックス化された値はC言語型のsptr_tという’スマートポインタ’である。 スマートポインタは通常のポインタと同じサイズを持つが、 1つのペアのタグビットで表わされる別の役割もおっている。 それはptypeと呼ばれる。
スマートポインタは一般に以下の形をしている:
-------------------------
| payload | GL|
-------------------------
G - セットされると、GCは値をポインタとして取り扱わない
L - 遅延。このビットがセットされると値はWHNFではないことを示す
sptr_tは以下の形の内どれか一つの状態にある:
-------------------------
| whnf raw value | 10|
-------------------------
-------------------------
| whnf location | 00|
-------------------------
WHNFは’Weak Head Normal Form’を表わしている。 さらにその値は中断された関数ではなく、それゆえサンクへのポインタでもない。 それは直接検査されるかもしれず、また評価される必要がないかもしれない。 wptr_tはsptr_tの別名で、上記の形式を取っていることを保証するためにある。 これは安全性を向上させるために使われ、 値がWHNFであり高価な’eval’をスキップできるかどうかを静的に知ることができる。
生の値とWHNF配置の違いは、 前者は未解釈のビットを保持しているが、 後者はヒープやスタックへのポインターであるためにGCが回収する必要があるということだ。 WHNF配置によって指し示されたメモリのフォーマットは実際の型表現に依存していていちがいに決まってはいない。
部分適用された関数は通常のWHNF値である。 ’eval’されたか更新された全適用された関数はサンクと呼ばれ、WHNFのポインタから指示されない。 これらの表現は以下である。
-------------------------
| lazy location | 01|
-------------------------
遅延配置はサンクもしくはWHNFへのリダイレクトトを指し示す。 遅延配置は必ずアロケートされたメモリ領域を差し、 その領域の先頭は制限されたスマートポインタである。 この制限されたスマートポインタはC言語の’fptr_t’型で表現される。 fptr_tは遅延配置の先頭エントリのためだけに存在し、 それらはオブジェクトとして流通することはない。
fptr_tはWHNF値もしくはコードへのポインタである可能性がある。 もしfptr_tが(上図2種類の内の一つのような)WHNF値ならそれはリダイレクションと呼ばれる。 遅延配置はそれがWHNFによるものだと厳密に取り扱わなければならない。 これは評価済みサンクへのリダイレクトとして使われる。
fptr_tはまた’コードポインタ’である可能性があり、 この場合遅延配置はサンクと呼ばれる。 コードポインタは実行可能な機械語へのポインタで、それはクロージャを評価してwptr_tを返す。 返却されたwptr_tは通常、サンクをリダイレクションに変更するために、コードポインタを書きつぶす。 指示されたコードはこのリダイレクションを実行する責任がある。
-------------------------
| code pointer | 11|
-------------------------
| data ... |
デバッグ時には特殊なコードポインタBLACK_HOLEが時々fptr_tに格納されている。 これはある種のランタイムエラーを検出する。
他の実装と異なりfptr_tは遅延配置の別の形では ない ことに注意すること。 あなたはリダイレクションをチェーンしてはいけない。 リダイレクションは常にWHNF値へのリダイレクションでなければならない。
sptr_t - タグ付けされたスマートポインタ。WHNF値か遅延配置を保持する。
wptr_t - タグ付けされたスマートポインタ。生か配置情報のWNHF値を保持する。
fptr_t - タグ付けされたスマートポインタ。リダイレクションを示すWHNF値かサンクを示すコードポインタを保持する。
jhcのコアは純粋なpure type systemにもとづいている。 pure type system (PTS)は型システムのパラメータ化されたセットである。 jhcのPTSについて以下で解説する。
種類 = (*, !, **, #, (#), ##, □)
公理 = (*:**, #:##, !:**, **:□, ##:□)
-- 種類kind(種)
* ボックス化値の種
! ボックス化された正格値の種
# アンボックス化された種
(#) アンボックス化タプルの種
-- 種類superkind
** 全てのボックス化値のsuperkind
## 全てのアンボックス化値のsuperkind
-- 種類box
□ superkindの存在位置
ユーザ定義の種の存在に加えて、supersort ##が存在する。
次に示すルール表は抽象の種類を表現している。 (A,B,C)の形をもつルールは種類Aから種類Bへの関数で結果が種類Cであることを意味している。 この文脈での 関数 は項と型レベルの抽象を含んでいる。
アンボックス化タプルを引数に取り正格であるかもしれないが、 関数はいつもボックス化されていることに注意すること。 コードを直接指示するポインタによって表現され、関数が正格であるなら、 関数の評価が中断された値であることはありえない。
これらの型システムのルールはλ抽象に適用される。 これらのルールによって、与えられた型を取れないデータコンストラクタが存在してしまうかもしれないが、それは許容できる。 例としてはアンボックス化タプルがある。 部分適用された関数であったとしてもそれらのコンストラクタが型エラーであることを正しく推論するのは困難ではない。
ショートカットとしてここでは *# を * と # の組み合わせをとして使う。
例えば、 (*#,*#,*) は (*,*,*) (#,*,*) (*,#,*) (#,#,*) のセットを意味している。
ルール =
(*#!,*#!,*) -- 値から値への関数で、ボックス化されていて非正格
(*#!,(#),*) -- 値からアンボックス化タプルへの関数で、ボックス化されていて非正格
((#),*#!,!) -- アンボックス化タプルから値への関数で、ボックス化されていて正格
((#),(#),!) -- アンボックス化タプルからアンボックス化タプルへの関数で、ボックス化されていて正格
(**,*,*) -- アンボックス化型から値への関数を保持している可能性がある
(**,#,*)
(**,!,*)
(**,**,**) -- 型から型への関数を持てる
(**,##,##) -- MutArray_ :: * -> #
(##,##,##) -- Complex_ :: # -> #
ボックス化値の定義は以下である。
_|_ :: t iff t::*
このPTSは関数的であるが単射ではない。
このPTSは次のレベルに階層化されている。
□ - 種類box
**,##, - 種類superkind
*,#,(#),! - 種類kind(種)
Int,Bits32_,Char - 種類type(型)
3,True,"bob" - 種類value(値)
ボックス化kind(* や !)は同型のランタイム表現を持つ型を表現する。 このため、関数はこれらのkindの型で多相的に記述することができる。 (**,?,?)という形のルールがあるため、ボックス化されたkindの型を引数として取ることができる。
アンボックス化されたkind # は独自のランタイム表現を持つ型に存在する。 アンボックス化型を多相的に使う関数を書くことはできない。
種類boxはコード中に現われないけれど、アンボックス化タプルのようなある種の型について理論的な視点から有用である。 アンボックス化タプルはボックス化/アンボックス化どちらの引数も取れる。 これを表現することは種類boxなしにそれは不可能だ。 なぜならsuperkind多相でなければならないから。 種類boxは次のような表現を許す。 (これはアンボックス化2タプルのケースである)
∀s1:□ ∀s2:□ ∀k1:s1 ∀k2:s2 ∀t1:k1 ∀t2:k2 . (# t1, t2 #)
たとえアンボックス化タプルが部分適用されることが妥当な型付けでも (##,?,?) もしくは (□,?,?) の形のルールは許容しないのだから、 この型は明らかに型検査されない。 アンボックス化タプルがいつも全適用されるという不変条件が強制されるなら種類boxのコード表現もまた不要になる。
種類の公理を見て接続の不足したグラフになっていることに、あなたは気付くかもしれない。
□ - box
/ \
** ## - superkind
/\ \
* ! # (#) - kind(種)
これは単に、それらのsuper-sort(種類)は本来不要であるが、kind (#) のアンボックス化タプルが多相性を欠如していることにに起因する。 このギャップを埋めるために(##)と(□)、□□の種類を導入できる。 これらの種類は決してコードや論文には現われないが、気にしないことにする。
□□ - 種類superbox
/ \
□ (□) - 種類box
/ \ \
** ## (##) - 種類superkind
/\ \ |
* ! # (#) - 種類kind(種)
後半の“jhcコア型システム”の章の翻訳は differential_engine (dif_engine) さんに手伝ってもらったでゲッソ。感謝でゲソー↩︎
blog comments powered by