In SML, how do I say that a particular exception is thrown?

Without actually trying to clone JUnit or anything else, I am compiling several utility functions to help test some SML code. I really know about QCheck, but it cannot do this alone and not what I want at all. (But if you know one more automated testing framework for SML, please talk.)

I would like to be able to assert that some function throws an exception, for example, when defining a function

fun broken x = raise Fail 

I would like to write something like

 throws ("ERROR: function is not broken enough!", fn () => broken 1, Fail) 

and raise an error if this function does not raise the expected exception.

I tried to write a throws function with type (string * exn * (unit -> unit)) -> unit as follows:

  fun throws (msg, e, func) = func () handle e' => if e = e' then () else raise ERROR (SOME msg) 

But this creates a bunch of compile-time errors, apparently because ML does not define equality by exception:

 sexp-tests.sml:54.31-57.49 Error: types of rules don't agree [equality type required] earlier rule(s): ''Z -> unit this rule: exn -> 'Y in rule: exn => raise exn sexp-tests.sml:54.31-57.49 Error: handler domain is not exn [equality type required] handler domain: ''Z in expression: func () handle e' => if e = e' then () else raise (ERROR <exp>) | exn => raise exn 

As a workaround, I suspect that I can simply reuse the existing assert function that I have:

 assert ((broken 1; false) handle Fail => true | _ => false) 

But this is a little more thinking and typing.

So, is there a way to write this throws function in SML?

+6
source share
1 answer

This next function should work:

 exception ERROR of string option; fun throwError msg = raise ERROR (SOME msg); fun throws (msg, func, e) = (func (); throwError msg) handle e' => if exnName e = exnName e' then () else raise throwError msg 

It uses the exnName function, which gets the exception name as a string and uses this instead of comparing.

More importantly, it also handles the case where an exception is not thrown at all, and also gives an error.

Alternatively, if you just need a boolean indicating whether an exception has been thrown, you can use:

 fun bthrows (func, e) = (func (); false) handle e' => exnName e = exnName e' 

Note that for the Fail case, you really need to instantiate the Fail exception, for example:

 throws ("ERROR: Oh no!", fn () => test 5, Fail "") 

Alternatively, you can take the name of the exception, for a cleaner general case:

 fun throws (msg, func, e) = (func (); throwError msg) handle e' => if e = exnName e' then () else raise throwError msg fun bthrows (func, e) = (func (); false) handle e' => e = exnName e' 

And then use it like this:

 throws ("ERROR: Oh no!", fn () => test 5, "Fail") 
+8
source

Source: https://habr.com/ru/post/887991/


All Articles