Вторая часть мерлезонского балета:
module Fact
use import mach.int.Int32
let rec fact (n : int32) : int32
variant { to_int n }
requires { to_int n >= 0 }
ensures { to_int result >= 1 }
=
if n = of_int 0 then of_int 1 else n * fact (n - of_int 1)
let test () = fact (of_int 50)
endСобственно тут все думаю понятно - вместо "безразмерного" int используется int32 из пакета mach.int.Int32 (mach тут для "машинный"). of_int/to_int - просто функции явного преобразования типов. При попытке откомпилировать оно ругается на функцию.
Замечу что если ensures убрать - все равно будет ругаться, потому что в пакете int32 переполнение считается ошибкой.
Способов пофиксить несколько - простейший - переопределить операцию * так, чтобы она выкидывала исключение при переполнении:
module Fact
use import mach.int.Int64 as I64
use import mach.int.Int32 as I32
exception Invalid_arg
let ( *) (a : int32) (b : int32) : int32
raises { Invalid_arg }
ensures { to_int a * to_int b = to_int result }
=
let res = I64.(*) (I64.of_int (I32.to_int a)) (I64.of_int (I32.to_int b)) in
if I64.(<=) res (I64.of_int max_int32) && I64.(>=) res (I64.of_int min_int32)
then I32.of_int (I64.to_int res)
else raise Invalid_arg
let rec fact (n : int32) : int32
variant { to_int n }
requires { 0 <= to_int n }
ensures { to_int result >= 1 }
raises { Invalid_arg }
=
if n = of_int 0 then of_int 1 else fact (n - of_int 1) * n
let test () raises { Invalid_arg } = fact (I32.of_int 50)
endВ комментариях тут пожалуй нуждается только ensures { to_int a * to_int b = to_int result } в определении операции *:
у WhyML подход тут довольно простой - он сам не пытается проявлять интеллект в контракты функций - потому тут надо сказать что * - это действительно умножение.
В случае с факториалом есть и более хитрый (но и более вообще говоря удобный при ее использовании) подход - но это уже сильно потом.
PS: Специально для Гастрита отмечу что сделать модуль IntMod32 не только можно - но и очень просто - часика два максимум писанины - просто описать аксиоматику соответствующего поля и все - просто наследуешься от algebra.OrderedCommutativeRing и описываешь свойства операций в этом поле.