Как преобразовать что-либо в строку, в 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Для получения дополнительной информации смотрите здесь