SML# - Resources/ProgrammingExamples/PolyC Diff

  • Added parts are displayed like this.
  • Deleted parts are displayed like this.

多くのC関数は多相的な関数である.これらを使いこなすことが,SML#でのCと
のシームレスな連携の鍵と言える.

その例として,Cの標準ライブラリ関数のfread(3)を見てみよう.
マニュアルには,以下のような型の関数と定義されている.
  #include <stdio.h>
  size_t  fread(void  *ptr, size_t size, size_t nmemb, FILE *stream);
この引数の型の意図は以下の通りである.
* void  *ptr 読み込むデータを格納する配列
* size_t size 読み込みデータの型tのサイズ
* size_t nmemb 最大要素数.
* FILE *stream 入力ストリーム.
すなわち,この関数は,第2引数に読み込むデータのサイズを指定することに
よって,種々の型のデータを読み込むことができる'多相的な関数'である.
したがって,MLのような型付き言語では,fread関数は,以下のような多相型
をもつ関数として扱えるの望ましい.
fread : 'a array * int * FILE -> int
ここで,各引数の意味は以下の通りである.
* 'a array 読み込むデータを格納する配列
* int 最大要素数.
* stdIn 入力ストリーム.
元々のC関数が要求していた読み込むデータのサイズは,型付き言語ではコン
パイラが計算し自動挿入されるのが理想である.

SML#では,その多相型レコード演算のコンパイル理論を基礎とし,
コンパイラが,すべての型をコンパイル時に計算し,必用に応じて,必用な関
数に自動的にサイズを受け渡すコンパイル方式を実現している.
そこで,この機構の一部を,FFIを使うプログラマに解放することにより,
freadのような型依存の動作を行う多相関数を型付き関数として安全に使用す
ることができる.

freadは以下のように宣言することによって使用できる.
  type c_file = unit ptr
  val libc = DynamicLink.dlopen "/lib/libc.so.6"
  val c_fread = DynamicLink.dlsym (libc, "fread")
  fun fread (dst, len, file) =
      _ffiapply c_fread (dst : 'a array, _sizeof('a), len : int, file : c_file)
                        : int
fread関数定義の本体の式は
_ffiapply e (arg1,...,argn) : t
の形をしたSML#の特殊構文である.
引数argには以下の二種類の式を書くことが出来る.
  exp : t
  _sizeof(t)
前者はC関数に渡す式とその型の宣言であり,後者は,型主導コンパイラが管
理する型tのサイズ情報の参照である.
この構文は,SML#の型主導のコンパイルの結果のコードをユーザが書くことに
相当する.

以上の宣言によりfread関数は以下の型の関数として定義される.
val fread = fn : ['a .'a Array.array * int * unit ptr  -> int]
これ以降,freadは多相関数として使用できる.当然,freadの呼び出しは
Cのfread関数の呼び出しとなり,引数で指定した配列に直接値が格納される.

freadに加え,fopen, fclose, fwrite等の関数を同様に定義すれば,
SML#から効率よいバイナリIOを型安全に使用することができる.