Как преобразовать что-либо в строку, в SML?
Я пытаюсь реализовать тестовую функцию для сравнения и отображения сообщения об ошибке, если они не равны:
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)
Как это исправить?
3 ответа:
В Haskell вы бы сделали свой тип экземпляром класса
Show
и реализовали перегруженный вариант функцииshow :: Show a => a -> String
, а затем напечаталиshow x
вместоx
. К сожалению, такого класса не существует в стандартном ML, и поэтому вы вынуждены писать свой собственный ненагруженный вариантshow
для каждого типа данных, который вы хотите распечатать.Некоторые компиляторы SML (по крайней мере, московские ML) поддерживают перегруженную функцию
makestring
, которая работает только для подмножества встроенных типов и не какие-то сложные типы. Например,makestring 2
иmakestring 2.0
оба работают, ноmakestring (0,0)
нет.Если вы хотите создать универсальную функцию утверждения, которая красиво выводит ошибку, вы можете создать тип данных с конструктором для каждого типа, значение которого вы хотите утверждать. Это будет работать как тип "union" в 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, "." ])
makestring присутствовал в некоторых ранних проектах стандарта 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
Это происходит потому, что типы actual и expect являются типами равенства, и это дает компилятору достаточно информация для правильной печати значений. В общем, однако, если PolyML.makestring входит в полиморфную функцию все, что будет напечатано-это "?". Решение состоит в том, чтобы передать дополнительный параметр, который является функцией для преобразования определенного типа в строку.
Затем вам нужно передать функцию, которая преобразует определенные значения в строки. В Poly / ML это может быть PolyML.makestring .fun assert(testName, actual, expect, toString) = if actual = expect then true else raise AssertionErrorException(testName ^ " failed. actual: " ^ toString actual ^ ", expect: " ^ toString expect );
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, ")"]);
Фактически вы реализуете классы типов, описанные в предыдущем ответе.
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
Для получения дополнительной информации смотрите здесь