I'm trying to implement a test function to compare and show error message if they are not equal:
exception AssertionErrorException of string
fun assert(testName, actual, expect) : bool =
if actual = expect
then true
else raise (AssertionErrorException (testName ^ " failed. actual: " ^ actual
^ ", expect: " ^ expect ));
Unfortunately, it doesn't work if I invoke it with non-string parameters:
assert("test1", SOME [], NONE);
It can't be compiled, and the error message is:
Error: operator and operand don't agree [tycon mismatch]
operator domain: string * string * string
operand: string * 'Z list option * 'Y option
in expression:
assert ("test1",SOME nil,NONE)
How to fix it?
In Haskell, you would make your type an instance of the typeclass Show
and implement an overloaded variant of the function show :: Show a => a -> String
and then print show x
rather than x
. Unfortunately such a typeclass does not exist in Standard ML, and so you are forced to write your own non-overloaded variant of show
for every datatype you want to pretty-print.
Some SML compilers (at least Moscow ML) support the overloaded function makestring
that only works for a subset of built-in types and not any composite types. E.g. makestring 2
and makestring 2.0
both work, but makestring (0,0)
does not. (Edit: David Matthews points out in an answer below that makestring
in PolyML is better.)
If you wish to make a generic assertion function that pretty-prints the error, one thing you could do is create a datatype with a constructor for each type you wish to assert the value of. This would work like the "union" type in C.
exception AssertionError of string
datatype assert = AssertInt of int
| AssertReal of real
| AssertBoolBool of bool * bool
| ...
fun assertPP (AssertInt i) = Int.toString i
| assertPP (AssertReal r) = Real.toString r
| assertPP (AssertBoolBool (b1,b2)) =
String.concat ["(", Bool.toString b1, ", ", Bool.toString b2, ")" ]
| assertPP (...) = ...
fun assert (testName, actual: assert, expect: assert) =
actual = expect (* ML infers equality for constructors *)
orelse raise AssertionError (String.concat
[ testName, " failed. actual: ", assertPP actual,
", expect: ", assertPP expect, "." ])
This is a poor man's replacement for overloading.