In my current F# project, I’ve employed a very simple library to support basic contract checking. It’s not fantastically clever, but I thought I’d post it here in case others find it useful.
To use the library, pipe values into the various functions of the “Is” module. For example…
let jz : OpcodeRoutine = fun (i, proc) ->
i.Operands.Length |> Is.EqualTo 1
i.HasBranchOffset |> Is.True
let x = i.Operands.[0] |> proc.GetOperandValue
let res = x = 0us
if i.BranchOffset.Condition = res then
proc.Branch i.BranchOffset
If either of the two preconditions highlighted above fail, a ContractFailureException will be thrown.
The full module is below. Again, the following code isn’t exactly rocket science, but I’ve found it dead useful, and you might, too.
exception ContractFailureException of string
[<RequiredModuleAccess>]
module Is =
let inline private fail message =
raise <| ContractFailureException(message)
let inline private failf fmt =
Printf.ksprintf fail fmt
let inline True condition =
if condition <> true then
fail "condition should be true."
let inline False condition =
if condition = true then
fail "condition should be false."
let inline Null obj =
if obj <> null then
fail "obj should be null."
let inline Some obj =
match obj with
| Some(_) -> ()
| None -> fail "obj should be Some."
let inline None obj =
match obj with
| Some(_) -> fail "obj should be None."
| None -> ()
let inline NotNull obj =
if obj = null then
fail "obj should not be null."
let inline EqualTo expected value =
if value <> expected then
failf "value should be equal to %A." expected
let inline NotEqualTo expected value =
if value = expected then
failf "value should not be equal to %A." expected
let inline LessThan high value =
if value >= high then
failf "value should be less than %A." high
let inline LessThanOrEqualTo high value =
if value > high then
failf "value should be less than or equal to %A." high
let inline GreaterThan low value =
if value <= low then
failf "value should be greater than %A." low
let inline GreaterThanOrEqualTo low value =
if value < low then
failf "value should be greater than or equal to %A." low
let inline InRange low high value =
if value < low || value > high then
failf "value should be in range %A to %A." low high
let inline NotInRange low high value =
if value >= low && value <= high then
failf "value should not be in range %A to %A." low high
let inline Empty (value : seq<_>) =
if not (value |> Seq.isEmpty) then
fail "value should be empty."
let inline NotEmpty (value : seq<_>) =
if value |> Seq.isEmpty then
fail "value should not be empty."
let inline OfType<'a> (value : obj) =
match value with
| :? 'a -> ()
| _ -> failf "value should be of type %s." typeof<'a>.FullName
let inline NotOfType<'a> (value : obj) =
match value with
| :? 'a -> failf "value should not be of type %s." typeof<'a>.FullName
| _ -> ()
Enjoy!