我试图实现一个测试函数来比较并显示错误消息,如果它们不相等:
exception AssertionErrorException of string
fun assert(testName, actual, expect) : bool =
if actual = expect
then true
else raise (AssertionErrorException (testName ^ " failed. actual: " ^ actual
^ ", expect: " ^ expect ));
不幸的是,如果我用非字符串参数调用它,它就不起作用了:
assert("test1", SOME [], NONE);
无法编译,错误信息为:
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)
如何解决?
答案 0 :(得分:6)
maketring 出现在标准ML的一些早期草稿中,但在最终版本之前被删除。 Poly / ML将其保留为 PolyML.makestring ,这适用于任何类型,包括结构化类型。
通过这个特殊的例子可以编写
fun assert(testName, actual, expect) =
if actual = expect
then true
else raise AssertionErrorException(testName ^ " failed. actual: " ^
PolyML.makestring actual ^ ", expect: " ^
PolyML.makestring expect);
所以
assert("test1", SOME [], NONE);
打印
Exception-
AssertionErrorException "test1 failed. actual: SOME [], expect: NONE"
raised
这恰好起作用,因为实际和期望的类型是相等类型,这为编译器提供了足够的信息来正确打印值。但是,一般情况下,如果 PolyML.makestring 包含在多态函数中,那么所有要打印的内容都是"?"。解决方案是传入一个额外的参数,该参数是将特定类型转换为字符串的函数。
fun assert(testName, actual, expect, toString) =
if actual = expect
then true
else raise AssertionErrorException(testName ^ " failed. actual: " ^
toString actual ^ ", expect: " ^ toString expect );
然后,您需要传入一个将特定值转换为字符串的函数。在Poly / ML中,这可以是 PolyML.makestring 。
assert("test2", (1,2,3), (1,2,4), PolyML.makestring);
打印
Exception-
AssertionErrorException
"test2 failed. actual: (1, 2, 3), expect: (1, 2, 4)" raised
如果你正在使用不同的SML实现,你仍然可以做同样的事情并传递你自己的特定类型的转换函数。
assert("test2", (1,2,3), (1,2,4),
fn (a,b,c) =>
String.concat["(", Int.toString a, ",", Int.toString b,
",", Int.toString c, ")"]);
实际上,您正在实现上一个答案中描述的类型类。
答案 1 :(得分:5)
structure Printf =
struct
fun $ (_, f) = f (fn p => p ()) ignore
fun fprintf out f = f (out, id)
val printf = fn z => fprintf TextIO.stdOut z
fun one ((out, f), make) g =
g (out, fn r =>
f (fn p =>
make (fn s =>
r (fn () => (p (); TextIO.output (out, s))))))
fun ` x s = one (x, fn f => f s)
fun spec to x = one (x, fn f => f o to)
val B = fn z => spec Bool.toString z
val I = fn z => spec Int.toString z
val R = fn z => spec Real.toString z
end
以下是一个使用示例。
val () = printf `"Int="I`" Bool="B`" Real="R`"\n" $ 1 false 2.0
这将打印以下内容。
Int=1 Bool=false Real=2.0
了解更多信息,请查看here
答案 2 :(得分:5)
在Haskell中,您可以将类型设置为类型类Show
的实例,并实现函数show :: Show a => a -> String
的重载变体,然后打印show x
而不是x
。遗憾的是,标准ML中不存在这样的类型类,因此您不得不为每个要打印的数据类型编写自己的非重载变体show
。
某些SML编译器(至少是Moscow ML)支持重载函数makestring
,它只适用于内置类型的子集,而不适用于任何复合类型。例如。 makestring 2
和makestring 2.0
都有效,但makestring (0,0)
没有。
如果您希望创建一个通用的断言函数来完美地打印错误,那么您可以做的一件事是创建一个数据类型,其中包含您希望断言其值的每种类型的构造函数。这将像C中的“union”类型一样工作。
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, "." ])
这是一个穷人替代超载。