SML: Какая разница между использованием абстипа и использованием подписи, чтобы скрыть реализацию структуры?

В прошлом я немного поработал на SML, но теперь я начинаю заниматься более интересными частями.

Используя конструкцию abstype...with...end, я могу создавать вещи, но не скрывать их детали реализации. Я также могу создать подпись того, что я хочу сделать, и использовать оператор :>, чтобы сделать структуру, придерживающуюся этой подписи, которая скрывает детали реализации.

Не являются ли подписи/структуры просто более общей версией abstypes? Что я могу сделать с абстипами, которые я не могу сделать с сигнатурами/структурами? Почему я хотел бы использовать abstype?

Заранее благодарим за помощь!

В качестве примера:

signature SET = sig
    type set
    val empty: set
    val insert: int * set -> set
    val member: int * set -> bool
end

structure Set :> SET = struct
    type set = int list
    val empty = []
    fun insert(x, s) = x::s
    fun member(x, []) = false
      | member(x, h::t) = (x = h) orelse member(x, t)
end

кажется, по крайней мере, столь же мощным, как

abstype AbsSet = absset of int list with
    val empty = absset([])
    fun insert(x, absset(s)) = absset(x::s)
    fun member(x, absset([])) = false
      | member(x, absset(h::t)) = (x = h) orelse member(x, absset(t))
end

Ответы

Ответ 1

Почему я хотел бы использовать abstype?

Начиная с самого простого, вы не будете. По крайней мере, я не могу придумать одну вескую причину.


Не являются ли подписи/структуры просто более общей версией abstypes?

Ну, я думаю, нам нужно взглянуть на историю SML. Непрозрачное (...: > ...) соответствие подписи не было частью SML '90, как описано в этом документе smlnj о модулях 1.3.9. непрозрачная подпись: >

... целью которого было создание "абстрактного" экземпляра сигнатуры SIG. Эта функция была исключена из SML '90 по разным причинам, но необходимость в ней была реальной.

Я не имею понятия о том, почему это не включает, но насколько я знаю, McQueen был "дальнейшим" абстипом, который был частью SML '90, и по какой-то причине не был удален в SML '97 ( возможно обратная совместимость?)

Однако существует принципиальная разница между ними, abstype является частью основного языка, где модули/сигнатуры/функторы являются частью модульной системы.


Что мне делать с абстипами, которые я не могу сделать с сигнатурами/структурами?

Я ничего не могу придумать. Тем не менее, я уверен, что было бы легко построить какой-то пример, который можно использовать с непрозрачной подпиткой и не может быть с abstypes.


UPDATE

Страница Деградированная абстипия в производную форму из вики-преемника-мл действительно содержит небольшое неофициальное описание о abstype, являющемся остатком.

Как и во многих других, они также относятся к разделам Дефекты в пересмотренном определении стандартного ML", в котором содержится подробная информация о некоторых "незначительные" ошибки/дефекты в определении abstype, хотя их связь мертва. "Пересмотренное определение стандартного ML" - это определение SML '97.

Ответ 2

Единственной важной причиной, по которой требуется abstype, является то, что реалистичные приложения - это довольно сложная печать. Это относится к пересечению SML/NJ и Poly/ML - я не знаю, как работает Mlton в этом отношении (у него нет надлежащего верхнего уровня).

Задача проста: определить абстрактный тип данных (тот, который не теряет равенства) и предоставить для него полноцветный принтер. Единственный (квази-портативный) ответ, который, как я знаю, использует простой старый абстип с непрозрачным подписями 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. (Вышеуказанные пост-ludes к определению структуры могут быть превращены в забавные обертки, которые ссылаются на SML для заполнения в момент компиляции таким образом, что это работает для обоих, что делает источники доступными.)

Обратите внимание, что для приложений, таких как Isabelle, HOL4, ProofPower, довольно сложная печать для крупноформатной печати, независимо от того, что могут сказать стандартные авторы 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));
*)

Надеюсь, что у меня все странные случаи. Синтаксис "no pp" отличается для разных SML: Poly/ML печатает исходное представление, а SML/NJ ничего не печатает (просто тире как держатель места). "Немаркированный" непрозрачный тип особенно неприятен: в Poly/ML он переопределит симпатичный принтер для int, но для SML/NJ он ничего не делает, что тоже плохо.