SML: В чем разница между использованием abstype и использованием подписи, чтобы скрыть реализацию структуры? - PullRequest
16 голосов
/ 04 сентября 2011

В прошлом я проделал небольшую работу в SML, но сейчас я начинаю переходить к более интересным частям.

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

Не являются сигнатурами / структурами простоболее общая версия абстипов?Что я могу сделать с абстипами, которые я не могу сделать с подписями / структурами?Зачем мне когда-либо хотеть использовать 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

Ответы [ 2 ]

9 голосов
/ 06 сентября 2011

Зачем мне использовать abstype?

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


Разве подписи / структуры не являются просто более общей версией абстипов?

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

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

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

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


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

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


ОБНОВЛЕНИЕ

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

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

3 голосов
/ 06 марта 2013

Единственная важная причина, по которой 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 он просто ничего не делает, что тоже плохо.

...