プログラミング言語SML#解説 4.0.0版
8 SML#の拡張機能:レコード多相性

8.5 レコードプログラミング例

多相レコード演算機能をもった言語(現在のところSML#しか ありませんが)では,レコード構造は,種々の属性に着目したモジュール性の高 いプログラムを型安全に構築していく上で大きな武器となります. これによって,多相関数による汎用性あるプログラムが,大規模なプロ グラム開発にスケールするようになります.

そのような例として,放物線を描いて落下する物体をプロットする場合 を考えてみましょう. 物体の位置は直交座標で表現するとします. そして,物体は,現在位置を表すX:realY:realのフィー ルドと現在の速度を表すを含むレコードVx:realVy:realのフィー ルドを含むレコードで表現することにします. これだけを決めておけば,物体のその他の属性や構造とは独立に,放物 線上を移動する関数を設計できます. 物体の運動は,物体を単位時間経過後の位置に移動させる関数ticを書き,この関数をくり返し呼び続ければ実現できます.

val tic : [’a#{X:real, Y:real, Vx:real, Vy:real}. ’a * real -> ’a]

簡単のために単位時間を1とします. 直行座標系での物体の移動ベクトルは,それぞれの座標のベクトルの合 成ですから,X軸とY軸について独立に関数を書きそれを合成すれば よいはずです. 位置は,それぞれ現在の位置に,単位時間あたりの移動距離,つまり速 度を加えればよいので,X軸方向はY軸方向それぞれ,独立に以下の ようにコードできます. (対話型セッションで型と共に示します.)

# fun moveX (p as {X:real, Vx:real,...}, t:real) = p # {X = X + Vx};
val moveX = fn : [’a#{Vx: real, X: real}. ’a * real -> ’a]
# fun moveY (p as {Y:real, Vy:real,...}, t:real) = p # {Y = Y + Vy};
val moveY = fn : [’a#{Vy: real, Y: real}. ’a * real -> ’a]

次に,それぞれの軸方向の速度を変更する関数を書きます. X軸方向は等速運動をし,Y軸方向は重力加速度による等加 速度運動をする場合の関数は,位置の変更と同様,以下のように簡単にコードで きます.

# fun accelerateX (p as {Vx:real,...}, t:real) = p;
val accelerateX = fn : [’a#{Vx: real}. ’a * real -> ’a]
# fun accelerateY (p as {Vy:real,...}, t:real) = p # {Vy = Vy + 9.8};
val accelerateY = fn : [’a#{Vy: real}. ’a * real -> ’a]

accelerateXは変化がないので,不要ですが,雛形として記述し ておくと将来の変更に便利です.

単位時間後の物体を求める関数ticは,以上の関数を合成するこ とによって得られます.

fun tic (p, t) =
    let
      val p = accelerateX (p, t)
      val p = accelerateY (p, t)
      val p = moveX (p, t)
      val p = moveY (p, t)
    in
      p
    end

このようにして得られたプログラムはモジュール性に富みかつ型安全で 堅牢なシステムとなります. たとえば,水平方向の速度は風の抵抗に現在の速度に対して10%減少 していく運動なども,accelerateY

# fun accelerateX (p as {Vx:real,...}, t:real) = p # {Vx = Vx * 0.90};
val accelerateX = fn : [’a#{Vx: real}. ’a * real -> ’a]

と変更するだけで実現できます.

このtic関数に対しては,本節の冒頭で書いた型が推論され,位 置と速度属性をもつ任意の物体に適用可能です.