2011-09-04 19 views
15

He trabajado un poco en SML en el pasado, pero ahora estoy empezando a llegar a las partes más interesantes.SML: ¿Cuál es la diferencia entre usar abstype y usar una firma para ocultar la implementación de una estructura?

Usando la construcción abstype...with...end, puedo hacer cosas pero mantener sus detalles de implementación ocultos. También puedo crear una firma de lo que quiero hacer, y usar el operador :> para hacer que una estructura se adhiera a esa firma que mantiene ocultos los detalles de implementación.

¿No son las firmas/estructuras solo una versión más general de abstypes? ¿Qué puedo hacer con abstypes que no puedo hacer con firmas/estructuras? ¿Por qué alguna vez querría usar abstype?

¡Gracias de antemano por la ayuda!

A modo de ejemplo:

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 

parece al menos tan potente como

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 

Respuesta

8

¿Por qué me va a querer utilizar abstype?

Empezando por el más fácil, no lo hará. Al menos no puedo encontrar una buena razón.


¿No son firmas/estructuras sólo una versión más general de abstypes?

Bueno, supongo que tenemos que echar un vistazo a la historia de SML. El opaco (...:> ...) la firma coincidente no era parte del SML '90 como se explica en este documento acerca de los módulos smlnj 1.3.9. opaque signature matching :>

... cuyo propósito era crear una instancia "abstracto" de la firma SIG. Esta característica se dejó fuera de SML '90 por varias razones, pero la necesidad era real.

que no tienen ni idea acerca de las razones para no incluir, pero por lo que yo sé McQueen era el más "lejos" de la abstype que era parte del SML '90 y por alguna razón no se eliminó al SML '97 (¿quizás compatibilidad hacia atrás?)

Sin embargo, existe una diferencia fundamental entre ellos, abstype es parte del lenguaje central donde los módulos/firmas/funtores son parte del sistema de módulos.


¿Qué puedo hacer con abstypes que no puedo hacer con firmas/estructuras?

No se me ocurre nada. Sin embargo, estoy bastante seguro de que sería fácil construir un ejemplo que pueda usar la coincidencia de firma opaca y no puede hacer con abstypes.


ACTUALIZACIÓN

La página Degrade abstype to derived form desde el wiki sucesor ml en realidad contiene una pequeña descripción informal sobre abstype ser un sobrante.

Como con muchos otros, también se refieren a las secciones del documento Defects in the Revised Definition of Standard ML que contiene detalles sobre algunos errores/defectos "menores" en la definición de abstype, aunque su enlace está muerto. La "Definición revisada de ML estándar" es la definición de SML '97.

+0

Enlace reparado ahora. :) –

+1

Lo único que puedes hacer con abstype que no puedes con los módulos (al menos en SML'97 simple) es definir un tipo de resumen localmente dentro de un '' let''. Pero eso no es algo que nadie haya sentido como una necesidad ardiente ... –

3

La única gran razón por la que todavía se requiere abstype es para aplicaciones realistas es imprimir bastante bien. Esto se refiere a la intersección de SML/NJ y Poly/ML - No sé cómo funciona Mlton en este aspecto (no tiene un nivel adecuado).

La tarea es simple: defina un tipo de datos abstracto (uno que no filtre la igualdad) y proporcione una impresora bonita para él. El único (cuasi-portátil) respuesta que conozco utiliza llanura de edad abstype con estilo SML'90 no opaca la firma coincidente:

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 debe imprimir {42}[1] en este ejemplo divertido - las líneas específicas del compilador necesitan ser no comentado Esto definitivamente está fuera del estándar SML'97, o intentos posteriores en ML'2000 y más allá, pero funciona tanto para SML/NJ como para Poly/ML, ya que todavía están disponibles en la actualidad. En cierto sentido, se ve una cultura SML'90 anterior y anterior a SML brillando, incluso un poco de pirateo de LPL hasta niveles avanzados. (Los post-ludes anteriores a la definición de estructura se pueden convertir en envoltorios graciosos que invocan el nivel SML en tiempo de compilación de una manera que funcione para ambos, haciendo que las fuentes parezcan portátiles.)

Tenga en cuenta que para aplicaciones como Isabelle , HOL4, ProofPower, toplevel ML, la impresión bonita es indispensable, independientemente de lo que digan los escritores estándar de SML.

Aquí hay dos versiones más que están más en línea con SML'97 y firma opaca a juego :> pero fallan a trabajar de manera uniforme:

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)); 
*) 

espero que tengo todos los casos impares derecha. La indicación "no pp" es diferente para diferentes SML: Poly/ML imprime la representación original, mientras que SML/NJ no imprime nada (solo un guión como marcador de posición). El tipo opaco "sin etiquetar" es particularmente desagradable: en Poly/ML anulará la impresora bonita para int, pero para SML/NJ no hace nada, lo que también es malo.

+0

Guau, esa es la primera vez que oigo hablar de eso. No estoy seguro de cómo me sentiría acerca de motivar una construcción vieja obsoleta por su interacción accidental con características no portátiles de implementaciones específicas. :) –

Cuestiones relacionadas