Concurrent Clean : CleanJ : ccall
Directoryモジュールを移植するにあたって、ccallというABCマシン命令の仕様を調査中。
ccall Cの関数名 "型指定"
という形式で記述され(型指定はクォートされる)、これがC言語の関数の呼び出しに置き換えられる。
型指定の部分は、次のような形式になる。
入力の型:出力の型:Cの関数に渡さない値の型
となっていて、1つの値に対する型は1文字で表現される。
I | 整数(Int) |
R | 実数(Real) |
S | 文字列(String) |
V | void |
A | ノード(A-value) |
ただし、voidは、出力にしか使えない。出力が複数ある場合には、1つ目が返値で、2つ目移行は引数のポインタ渡しを利用して、値の受け渡しを行う。
void g (int,int *,int *); f :: !Int !*Int -> (!Int,!Int,!*Int); f a1 a2 = code { ccall g "I:VII:I" };
とある場合、「f :: a1 a2 -> b1 b2 b3」のように名前を付けると、「g (a1, *b1, *b2)」のように関数呼び出しが行われ、a2は何も変更されないままb3になる。
-
-
- -
-
さて、これをCleanJで実装するには、Javaにはポインタはないので、全て返値で値を返してやる必要がある。最も単純で汎用的なのは、返値を配列にしてやることだろう。ただし、返値が1つだけの場合は配列化しないで返す方が効率的だし、同じ種類の基本値だけを複数返す場合はObject[]よりも基本値の配列を使う方が効率的だ。
ただし、そういうやり方だと、メソッドの型からhtocleanのように自動的にccallを生成することは難しくなる。
L.L.Ring
でプレゼント引き換えでもらったボールが、飼猫の遊び道具になっている。
昔は、あまりボール大のものに興味を持たない猫だったのだけれど、最近はそういうものも興味を持つようになったらしい。
Haskell : モナドの実行順序は何によって規定されているのだろうか?
[id:sshi:20060903:p3]より。
私も、bind(>>=)の実装が実行順序を規定していると思います。
http://www.geocities.jp/lethevert/softwares/clean/gettingStarted19.html
上で考察していますが、bind(>>=)で引数を正格評価しないと、意図とは逆の順に評価されてしまうのではないかと思います。
Haskellには、Cleanの正格性注釈のような便利なものはないので、どのように正格評価を強制しているかというと、
http://www.sampou.org/haskell/a-a-monads/html/
の説明では、付けたしですよ、と説明されている「失敗」の存在が正格評価を行わせるキーポイントになっているのではないかと思います。
つまり、
instance Monad Maybe where Nothing >>= f = Nothing (Just x) >>= f = f x return = Just
のように、bind(>>=)の実装には、失敗を判定するためのパターンマッチが存在するので、関数適用する際に値が必要になるため、bind(>>=)の引数が正格評価されているのではないかと思います。
だから、失敗のないモナドを作れば、逆順に評価が進められるモナドになるのではないかと想像します。
fold, pack, instantiate
Recursive Type : fold, unfold
Existential Type : pack, unpack
Universal Type : instantiate
という用語の対応。
Concurrent Clean : マルチスレッドとグラフの共有
Cleanでマルチスレッドプログラミングを実装するに置いては、気になるのがグラフの共有。
一見、純粋関数型言語なので全部immutableだからいいじゃん、と思うかもしれないが、Erlangのようにeagerに評価しないので、あるグラフが未評価であった場合に、複数のスレッドでグラフを共有すると排他制御を考える必要が出てくる。
それでは、式の評価をすべてsynchronizedしていしてやれば安全になるが、それでは同期化のための無駄なオーバーヘッドが多く発生してしまう。そこで、必要な部分だけ、明示的に同期化するように指定してやる必要がある。
::Synchronized a = Syncronized a synchronize :: *a -> *(Synchronized a) ... # s = synchronize (['a '] ++ ['message '] ++ ['to send']) = send_message s unit
というように、synchronizeという関数を用意して、これを通すことで、グラフにマークをつけて同期化を強制するというアイデアは上手くいきそうだ。ただし、synchronizeという関数は、実は通常の関数ではなく、システムで用意する特殊なプリミティブとして提供する。
ところで、次のように、一意性型属性を付けてやることで、共有を不可にするというアイデアは上手く行かない。
send_message :: *a Unit -> Unit
というのは、「*{a::[Int]}」というようにレコード型にリストを包んだときには、一意になるのは外側のレコードだけであって、内側のリストは共有することができる。内側のリストまで一意にするには「*{a::*[Int]}」とする必要があるが、これは、関数の型としては指定できない。