Единственная важная причина, по которой abstype
все еще требуется для реалистичных приложений - это многоуровневая печать. Это относится к пересечению SML / NJ и Poly / ML - я не знаю, как Млтон работает в этом отношении (у него нет надлежащего верхнего уровня).
Задача проста: определить абстрактный тип данных (тот, который не пропускает равенство) и предоставить для него симпатичный принтер высокого уровня. Единственный известный мне (квазипереносимый) ответ использует простой старый абстип с сопоставлением непрозрачных сигнатур в стиле SML'90:
structure A1:
sig
type t val a: t val b: t -> t val print: t -> string
end =
struct
abstype t = A of int
with
val a = A 42
fun b (A i) = A (i + 1)
fun print (A i) = "{" ^ Int.toString i ^ "}[1]"
end
end;
(* works for Poly/ML 5.3, 5.4, 5.5:
PolyML.addPrettyPrinter (fn depth => fn pretty => fn x => PolyML.PrettyString (A1.print x));
*)
(* works for SML/NJ 110.xx:
CompilerPPTable.install_pp ["A1", "t"] (fn pps => fn x => PrettyPrint.string pps (A1.print x));
*)
A1.a
должен вывести {42}[1]
в этом забавном примере - специфичные для компилятора строки должны быть без комментариев. Это определенно выходит за рамки стандарта SML'97 или более поздних попыток для ML'2000 и выше, но работает как для SML / NJ, так и для Poly / ML, поскольку они все еще доступны сегодня. В некотором смысле вы видите, как просвечивает какая-то старая культура SML'90 и pre-SML, даже небольшая часть LISP-взлома. (Вышеупомянутые пост-луды к определению структуры могут быть превращены в забавные обертки, которые вызывают уровень SML во время компиляции таким образом, который работает для обоих, таким образом делая исходные коды переносимыми.)
Обратите внимание, что для таких приложений, как Isabelle, HOL4, ProofPower, очень важна печать ML, независимо от того, что скажут стандартные писатели SML.
Вот еще две версии, которые в большей степени соответствуют SML'97 и непрозрачному сопоставлению сигнатур :>
, но не работают равномерно:
structure A2 :>
sig
type t val a: t val b: t -> t val print: t -> string
end =
struct
datatype t = A of int
val a = A 42
fun b (A i) = A (i + 1)
fun print (A i) = "{" ^ Int.toString i ^ "}[2]"
(* works, but non-portable:
val _ =
PolyML.addPrettyPrinter (fn depth => fn pretty => fn x => PolyML.PrettyString (print x))
*)
(* does not work (scope problem -- no pp):
val _ =
CompilerPPTable.install_pp ["A2", "t"] (fn pps => fn x => PrettyPrint.string pps (A2.print x));
*)
end;
(* does not work (no pp):
PolyML.addPrettyPrinter (fn depth => fn pretty => fn x => PolyML.PrettyString (A2.print x));
*)
(* does not work (no pp):
CompilerPPTable.install_pp ["A2", "t"] (fn pps => fn x => PrettyPrint.string pps (A2.print x));
*)
structure A3 :>
sig
type t val a: t val b: t -> t val print: t -> string
end =
struct
type t = int
val a = 42
fun b i = i + 1
fun print i = "{" ^ Int.toString i ^ "}[3]"
(* does not work (overrides pp for int):
val _ =
PolyML.addPrettyPrinter (fn depth => fn pretty => fn x => PolyML.PrettyString (print x))
*)
(* does not work (scope problem -- no pp)
val _ = CompilerPPTable.install_pp ["A2", "t"] (fn pps => fn x => PrettyPrint.string pps (A2.print x));
*)
end;
(* works:
PolyML.addPrettyPrinter (fn depth => fn pretty => fn x => PolyML.PrettyString (A3.print x));
*)
(* does not work (no pp):
CompilerPPTable.install_pp ["A3", "t"] (fn pps => fn x => PrettyPrint.string pps (A3.print x));
*)
Я надеюсь, что я правильно понял все странные случаи. Ситуация «без пп» различна для разных SML: Poly / ML печатает исходное представление, в то время как SML / NJ ничего не печатает (только тире в качестве заполнителя). Непрозрачный непрозрачный тип особенно неприятен: в Poly / ML он переопределяет симпатичный принтер для int, но для SML / NJ он просто ничего не делает, что тоже плохо.