Articulo de referencia

ML estándar

{{Cite web |last=Harper |first=Robert |author-link=Robert Harper (computer scientist) |date=May 5, 1998 |title=Programming in Standard ML |url=https://www.cse.iitd.ac.in/~sak/co...

Standard ML ( SML ) es un lenguaje de programación funcional , modular , de alto nivel y de propósito general , con verificación e inferencia de tipos en tiempo de compilación . Es popular para escribir compiladores , para la investigación de lenguajes de programación y para desarrollar demostradores de teoremas .

Standard ML es un dialecto moderno de ML , el lenguaje utilizado en el proyecto de demostración de teoremas Logic for Computable Functions (LCF). Se distingue de otros lenguajes ampliamente utilizados por tener una especificación formal , dada como reglas de tipado y semántica operacional en The Definition of Standard ML , publicada originalmente en 1990, con una segunda y última revisión publicada en 1997. [ 5 ] [ 6 ]

Idioma

Standard ML es un lenguaje de programación funcional con algunas características impuras. Los programas escritos en Standard ML consisten en expresiones en contraste con sentencias o comandos, aunque algunas expresiones de tipo unidad solo se evalúan por sus efectos secundarios .

Funciones

Como en todos los lenguajes funcionales, una característica clave de Standard ML es la función , que se utiliza para la abstracción. La función factorial se puede expresar de la siguiente manera:

función factorial n = si n = 0 entonces 1 sino n * factorial ( n - 1 )

Inferencia de tipo

Un compilador SML debe inferir el tipo estático sin anotaciones de tipo proporcionadas por el usuario. Debe deducir que solo se usa con expresiones enteras y, por lo tanto, debe ser un entero, y que todas las expresiones terminales son expresiones enteras.valfactorial:int->intn

Definiciones declarativas

La misma función puede expresarse con definiciones de funciones clausales donde la condición if - then - else se reemplaza con plantillas de la función factorial evaluada para valores específicos:

función factorial 0 = 1 | factorial n = n * factorial ( n - 1 )

Definiciones imperativas

o de forma iterativa:

fun factorial n = let val i = ref n and acc = ref 1 in while !i > 0 do ( acc := !acc * !i ; i := !i - 1 ); !acc end

Funciones Lambda

o como una función lambda:

val rec factorial = fn 0 => 1 | n => n * factorial ( n - 1 )

Aquí, la palabra clave valintroduce una vinculación de un identificador a un valor, fnintroduce una función anónima y recpermite que la definición sea autorreferencial.

Definiciones locales

La encapsulación de un bucle recursivo de cola ajustado que preserva invariantes con uno o más parámetros acumuladores dentro de una función externa libre de invariantes, como se ve aquí, es un modismo común en Standard ML.

Utilizando una función local, se puede reescribir en un estilo recursivo de cola más eficiente:

local fun loop ( 0 , acc ) = acc | loop ( m , acc ) = loop ( m - 1 , m * acc ) in fun factorial n = loop ( n , 1 ) end

Sinónimos de tipo

Un sinónimo de tipo se define con la palabra clave type. Aquí se muestra un sinónimo de tipo para puntos en un plano , funciones que calculan las distancias entre dos puntos y el área de un triángulo con vértices dados según la fórmula de Herón . (Estas definiciones se utilizarán en ejemplos posteriores).

tipo loc = real * realcuadrado divertido ( x : real ) = x * xfun dist ( x , y ) ( x' , y' ) = Math . sqrt ( square ( x' - x ) + square ( y' - y ))fun heron ( a , b , c ) = let val x = dist a b val y = dist b c val z = dist a c val s = ( x + y + z ) / 2.0 in Math . sqrt ( s * ( s - x ) * ( s - y ) * ( s - z )) end

Tipos de datos algebraicos

Standard ML ofrece un sólido soporte para los tipos de datos algebraicos (TDA). Un tipo de dato puede considerarse como una unión disjunta de tuplas (o una "suma de productos"). Son fáciles de definir y de usar, principalmente gracias a la coincidencia de patrones y a la comprobación de exhaustividad y redundancia de patrones que incorporan la mayoría de las implementaciones de Standard ML.

En los lenguajes de programación orientados a objetos , una unión disjunta puede expresarse como jerarquías de clases . Sin embargo, a diferencia de las jerarquías de clases , los TAD son cerrados . Por lo tanto, la extensibilidad de los TAD es ortogonal a la extensibilidad de las jerarquías de clases. Las jerarquías de clases pueden extenderse con nuevas subclases que implementan la misma interfaz, mientras que las funciones de los TAD pueden extenderse para el conjunto fijo de constructores. Véase el problema de la expresión .

Un tipo de dato se define con la palabra clave datatype, como en:

tipo de datos forma = Círculo de loc * real (* centro y radio *) | Cuadrado de loc * real (* esquina superior izquierda y longitud del lado; alineado con el eje *) | Triángulo de loc * loc * loc (* vértices *)

Tenga en cuenta que un sinónimo de tipo no puede ser recursivo; los tipos de datos son necesarios para definir constructores recursivos. (Esto no es relevante en este ejemplo).

Coincidencia de patrones

Los patrones se comparan en el orden en que se definen. Los programadores de C pueden usar uniones etiquetadas , con despacho basado en valores de etiqueta, para lograr lo que ML hace con los tipos de datos y la coincidencia de patrones. Sin embargo, si bien un programa en C con las comprobaciones adecuadas será, en cierto modo, tan robusto como el programa ML correspondiente, dichas comprobaciones serán necesariamente dinámicas; las comprobaciones estáticas de ML ofrecen garantías sólidas sobre la corrección del programa en tiempo de compilación.

Los argumentos de una función se pueden definir como patrones de la siguiente manera:

área de diversión ( Círculo (_, r )) = Matemáticas . pi * cuadrado r | área ( Cuadrado (_, s )) = cuadrado s | área ( Triángulo p ) = garza p (* ver arriba *)

La denominada "forma clausal" de definición de función, donde los argumentos se definen como patrones, es simplemente azúcar sintáctico para una expresión de caso:

forma de área divertida = forma de caso de Círculo (_, r ) => Matemáticas . pi * cuadrado r | Cuadrado (_, s ) => cuadrado s | Triángulo p => garza p

Verificación exhaustiva

La comprobación de exhaustividad de patrones garantizará que cada constructor del tipo de dato coincida con al menos un patrón.

El siguiente patrón no es exhaustivo:

fun center ( Circle ( c , _)) = c | center ( Square (( x , y ), s )) = ( x + s / 2.0 , y + s / 2.0 )

No hay ningún patrón para el Trianglecaso en la centerfunción. El compilador emitirá una advertencia de que la expresión del caso no es exhaustiva, y si Trianglese pasa un a a esta función en tiempo de ejecución, se generará una excepción.exceptionMatch

Verificación de redundancia

El patrón en la segunda cláusula de la siguiente función (sin sentido) es redundante:

fun f ( Circle (( x , y ), r )) = x + y | f ( Circle _) = 1.0 | f _ = 0.0

Cualquier valor que coincida con el patrón de la segunda cláusula también coincidiría con el patrón de la primera cláusula, por lo que la segunda cláusula es inaccesible. Por lo tanto, esta definición en su conjunto presenta redundancia y genera una advertencia en tiempo de compilación.

La siguiente definición de función es exhaustiva y no redundante:

val hasCorners = fn ( Circle _) => false | _ => true

Si el control pasa el primer patrón ( Circle), sabemos que la forma debe ser o Square. TriangleEn cualquiera de esos casos, sabemos que la forma tiene esquinas, por lo que podemos regresar truesin discernir la forma real.

Funciones de orden superior

Las funciones pueden consumir otras funciones como argumentos:

función map f ( x , y ) = ( f x , f y )

Las funciones pueden producir funciones como valores de retorno:

constante de función k = ( fn _ => k )

Las funciones también pueden consumir y producir funciones:

fun compose ( f , g ) = ( fn x => f ( g x ))

La función List.mapde la biblioteca base es una de las funciones de orden superior más utilizadas en Standard ML:

mapa divertido _ [] = [] | mapa f ( x :: xs ) = f x :: mapa f xs

Una implementación más eficiente con recursión de cola List.foldl:

fun map f = List . rev o List . foldl ( fn ( x , acc ) => f x :: acc ) []

Excepciones

Las excepciones se generan con la palabra clave raisey se manejan con la handleconstrucción de coincidencia de patrones. El sistema de excepciones puede implementar una salida no local ; esta técnica de optimización es adecuada para funciones como la siguiente.

excepción local Cero ; val p = fn ( 0 , _) => raise Cero | ( a , b ) = > a * b en fun prod xs = List.foldl p 1 xs handle Cero = > 0 fin

Cuando se produce esta excepción, el control abandona la función por completo. Consideremos la alternativa: se devolvería el valor 0, se multiplicaría por el siguiente entero de la lista, se devolvería el valor resultante (inevitablemente 0), y así sucesivamente. La generación de la excepción permite que el control omita toda la cadena de marcos y evite el cálculo asociado. Nótese el uso del guion bajo ( ) como comodín.exceptionZeroList.foldl_

La misma optimización se puede obtener con una llamada de cola .

local fun p a ( 0 :: _) = 0 | p a ( x :: xs ) = p ( a * x ) xs | p a [] = a in val prod = p 1 end

Sistema de módulos

El avanzado sistema de módulos de Standard ML permite descomponer los programas en estructuras jerárquicamente organizadas con definiciones de tipo y valor lógicamente relacionadas. Los módulos proporcionan no solo control del espacio de nombres , sino también abstracción, ya que permiten definir tipos de datos abstractos . El sistema de módulos se compone de tres construcciones sintácticas principales: firmas, estructuras y functores.

Firmas

Una firma es una interfaz , generalmente considerada como un tipo para una estructura; especifica los nombres de todas las entidades proporcionadas por la estructura, la aridad de cada componente de tipo, el tipo de cada componente de valor y la firma de cada subestructura. Las definiciones de los componentes de tipo son opcionales; los componentes de tipo cuyas definiciones están ocultas son tipos abstractos .

Por ejemplo, la firma para una cola puede ser:

firma COLA = tipo de firma 'a cola excepción QueueError ; val vacío : 'a cola val estáEmpty : 'a cola -> bool val singleton : 'a -> 'a cola val deLista : 'a lista -> 'a cola val insertar : 'a * 'a cola -> 'a cola val mirar : 'a cola -> 'a val eliminar : 'a cola -> 'a * 'a cola fin

Esta firma describe un módulo que proporciona un tipo polimórfico , y valores que definen operaciones básicas en colas.'aqueueexceptionQueueError

Estructuras

Una estructura es un módulo; consiste en una colección de tipos, excepciones, valores y estructuras (llamadas subestructuras ) agrupados en una unidad lógica.

Una estructura de cola se puede implementar de la siguiente manera:

estructura TwoListQueue :> QUEUE = struct type 'a queue = 'a list * 'a listexcepción QueueError ;val vacío = ([], [])fun isEmpty ([], []) = true | isEmpty _ = falsefun singleton a = ([], [ a ])fun fromList a = ([], a )fun insert ( a , ([], [])) = singleton a | insert ( a , ( ins , outs )) = ( a :: ins , outs )fun peek (_, []) = raise QueueError | peek ( ins , outs ) = List . hd outsfun remove (_, []) = raise QueueError | remove ( ins , [ a ]) = ( a , ([], List . rev ins )) | remove ( ins , a :: outs ) = ( a , ( ins , outs )) end

Esta definición declara que implementa . Además, la atribución opaca denotada por indica que cualquier tipo que no esté definido en la firma (es decir ) debe ser abstracto, lo que significa que la definición de una cola como un par de listas no es visible fuera del módulo. La estructura implementa todas las definiciones de la firma.structureTwoListQueuesignatureQUEUE:>type'aqueue

Se puede acceder a los tipos y valores de una estructura mediante la "notación de puntos":

val q : string TwoListQueue . queue = TwoListQueue . empty val q' = TwoListQueue . insert ( Real . toString Math . pi , q )

Funtores

Un functor es una función que transforma estructuras en otras estructuras; es decir, un functor acepta uno o más argumentos, que suelen ser estructuras con una firma determinada, y produce una estructura como resultado. Los functores se utilizan para implementar estructuras de datos y algoritmos genéricos .

Un algoritmo popular para la búsqueda en anchura de árboles utiliza colas. [ 7 ] Aquí hay una versión de ese algoritmo parametrizada sobre una estructura de cola abstracta:

(* según Okasaki, ICFP, 2000 *) functor BFS ( Q : COLA ) = struct datatype 'a tree = E | T of 'a * 'a tree * 'a treelocal fun bfsQ q = if Q . isEmpty q then [] else search ( Q . remove q ) and search ( E , q ) = bfsQ q | search ( T ( x , l , r ), q ) = x :: bfsQ ( insert ( insert q l ) r ) and insert q a = Q . insert ( a , q ) in fun bfs t = bfsQ ( Q . singleton t ) end endestructura QueueBFS = BFS ( TwoListQueue )

Dentro de la cola, la representación de la misma no es visible. Más concretamente, no hay forma de seleccionar la primera lista en la cola de dos listas, si es que esa es la representación que se está utilizando. Este mecanismo de abstracción de datos hace que la búsqueda en amplitud sea verdaderamente independiente de la implementación de la cola. Esto es deseable en general; en este caso, la estructura de la cola puede mantener con seguridad cualquier invariante lógica de la que dependa su corrección, protegida por la sólida barrera de la abstracción.functorBFS

Ejemplos de código

Los fragmentos de código SML se estudian más fácilmente introduciéndolos en un entorno interactivo de nivel superior .

¡Hola Mundo!

El siguiente es un programa "¡Hola, mundo!" :

Algoritmos

Ordenación por inserción

La ordenación por inserción (ascendente) se puede expresar de forma concisa de la siguiente manera:intlist

fun insert ( x , []) = [ x ] | insert ( x , h :: t ) = sort x ( h , t ) and sort x ( h , t ) = if x < h then [ x , h ] @ t else h :: insert ( x , t ) val insertionsort = List . foldl insert []

Mergesort

Aquí, el algoritmo clásico de ordenación por fusión se implementa en tres funciones: split, merge y mergesort. También observe la ausencia de tipos, con la excepción de la sintaxis y que denotan listas. Este código ordenará listas de cualquier tipo, siempre que se defina una función de ordenación consistente. Usando la inferencia de tipos de Hindley-Milner , se pueden inferir los tipos de todas las variables, incluso tipos complicados como el de la función .op::[]cmpcmp

Dividir

funsplitse implementa con un cierre con estado que alterna entre truey false, ignorando la entrada:

fun alternator {} = let val estado = ref verdadero en fn a => !estado antes de estado := no ( !estado ) fin(* Divide una lista en dos mitades casi iguales que tendrán la misma longitud, * o la primera tendrá un elemento más que la otra. * Se ejecuta en tiempo O(n), donde n = |xs|. *) fun split xs = List . partition ( alternator {}) xs

Unir

Merge utiliza un bucle de función local para mayor eficiencia. El bucle interno loopse define en términos de casos: cuando ambas listas no están vacías ( ) y cuando una lista está vacía ( ).x::xs[]

Esta función combina dos listas ordenadas en una sola. Observe cómo el acumulador accse construye hacia atrás y luego se invierte antes de devolverse. Esta es una técnica común, ya que se representa como una lista enlazada ; esta técnica requiere más tiempo de reloj, pero el comportamiento asintótico no es peor.'alist

(* Combina dos listas ordenadas usando el comando `cmp`. * Pre: cada lista debe estar ordenada previamente por cada cmp. * Se ejecuta en tiempo O(n), donde n = |xs| + |ys|. *) fun merge cmp ( xs , []) = xs | merge cmp ( xs , y :: ys ) = let fun loop ( a , acc ) ( xs , []) = List . revAppend ( a :: acc , xs ) | loop ( a , acc ) ( xs , y :: ys ) = if cmp ( a , y ) then loop ( y , a :: acc ) ( ys , xs ) else loop ( a , y :: acc ) ( xs , ys ) in loop ( y , []) ( ys , xs ) end

Mergesort

La función principal:

fun ap f ( x , y ) = ( f x , f y )(* Ordena una lista según la operación de ordenación cmp dada. * Se ejecuta en tiempo O(n log n), donde n = |xs|. *) fun mergesort cmp [] = [] | mergesort cmp [ x ] = [ x ] | mergesort cmp xs = ( merge cmp o ap ( mergesort cmp ) o split ) xs

Ordenación rápida

Quicksort se puede expresar de la siguiente manera. es un cierre que consume un operador de orden .funpartop<<

infijo <<fun quicksort ( op << ) = let fun part p = List . partition ( fn x => x << p ) fun sort [] = [] | sort ( p :: xs ) = join p ( part p xs ) and join p ( l , r ) = sort l @ p :: sort r in sort end

intérprete de expresiones

Nótese la relativa facilidad con la que se puede definir y procesar un lenguaje de expresiones pequeño:

excepción TyErr ;tipo de dato ty = IntTy | BoolTyfun unify ( IntTy , IntTy ) = IntTy | unify ( BoolTy , BoolTy ) = BoolTy | unify (_, _) = raise TyErrtipo de dato exp = Verdadero | Falso | Entero de entero | No de exp | Suma de exp * exp | Si de exp * exp * expfun infer True = BoolTy | infer False = BoolTy | infer ( Int _) = IntTy | infer ( Not e ) = ( assert e BoolTy ; BoolTy ) | infer ( Add ( a , b )) = ( assert a IntTy ; assert b IntTy ; IntTy ) | infer ( If ( e , t , f )) = ( assert e BoolTy ; unify ( infer t , infer f )) and assert e t = unify ( infer e , t )fun eval True = True | eval False = False | eval ( Int n ) = Int n | eval ( Not e ) = if eval e = True then False else True | eval ( Add ( a , b )) = ( case ( eval a , eval b ) of ( Int x , Int y ) => Int ( x + y )) | eval ( If ( e , t , f )) = eval ( if eval e = True then t else f )fun run e = ( inferir e ; ALGUNOS ( evaluar e )) manejar TyErr => NINGUNO

Ejemplos de uso en expresiones bien tipadas y mal tipadas:

val SOME ( Int 3 ) = run ( Add ( Int 1 , Int 2 )) (* bien tipado *) val NONE = run ( If ( Not ( Int 1 ), True , False )) (* mal tipado *)

Números enteros de precisión arbitraria

El IntInfmódulo proporciona aritmética de enteros de precisión arbitraria. Además, los literales enteros pueden utilizarse como enteros de precisión arbitraria sin que el programador tenga que hacer nada.

El siguiente programa implementa una función factorial de precisión arbitraria:

Aplicación parcial

Las funciones currificadas tienen muchas aplicaciones, como la eliminación de código redundante. Por ejemplo, un módulo puede requerir funciones de tipo , pero es más conveniente escribir funciones de tipo donde existe una relación fija entre los objetos de tipo y . Una función de tipo puede factorizar esta característica común. Este es un ejemplo del patrón adaptador .a->ba*c->bacc->(a*c->b)->a->b

En este ejemplo, se calcula la derivada numérica de una función dada en el punto :fundfx

- fun d delta f x = ( f ( x + delta ) - f ( x - delta )) / ( 2.0 * delta ) val d = fn : real -> ( real -> real ) -> real -> real

El tipo indica que asigna un "float" a una función con el tipo . Esto nos permite aplicar parcialmente argumentos, lo que se conoce como currificación . En este caso, la función se puede especializar aplicándola parcialmente con el argumento . Una buena opción para cuando se utiliza este algoritmo es la raíz cúbica del épsilon de la máquina .fund(real->real)->real->realddeltadelta

- val d' = d 1E~8 ; val d' = fn : ( real -> real ) -> real -> real

El tipo inferido indica que d'espera una función con el tipo como su primer argumento. Podemos calcular una aproximación a la derivada dereal->realF(incógnita)=incógnita3incógnita1{\displaystyle f(x)=x^{3}-x-1}enincógnita=3{\displaystyle x=3}La respuesta correcta esF(3)=271=26{\displaystyle f'(3)=27-1=26}.

- d' ( fn x => x * x * x - x - 1.0 ) 3.0 ; val it = 25.9999996644 : real

Bibliotecas

Estándar

La biblioteca Basis [ 8 ] ha sido estandarizada y se incluye con la mayoría de las implementaciones. Proporciona módulos para árboles, matrices y otras estructuras de datos, así como interfaces de entrada/salida y del sistema.

Tercero

Para computación numérica , existe un módulo Matrix (pero actualmente está roto), https://www.cs.cmu.edu/afs/cs/project/pscico/pscico/src/matrix/README.html .

Para gráficos, cairo-sml es una interfaz de código abierto para la biblioteca gráfica Cairo . Para aprendizaje automático, existe una biblioteca para modelos gráficos.

Implementaciones

Las implementaciones de Standard ML incluyen las siguientes:

Estándar

  • HaMLet : un intérprete de Standard ML que pretende ser una implementación de referencia precisa y accesible del estándar.
  • MLton ( mlton.org ): un compilador optimizador de programas completos que se ajusta estrictamente a la Definición y produce código muy rápido en comparación con otras implementaciones de ML, incluidos los backends para LLVM y C.
  • Moscow ML : una implementación ligera, basada en el motor de ejecución Caml Light que implementa el lenguaje Standard ML completo, incluyendo módulos y gran parte de la biblioteca base.
  • Poly/ML : una implementación completa de Standard ML que produce código rápido y admite hardware multinúcleo (a través de subprocesos de la Interfaz de Sistema Operativo Portátil ( POSIX )); su sistema de tiempo de ejecución realiza recolección de basura en paralelo y compartición en línea de subestructuras inmutables.
  • Standard ML de Nueva Jersey ( smlnj.org ): un compilador completo, con bibliotecas asociadas, herramientas, un intérprete interactivo y documentación con soporte para ML concurrente.
  • SML.NET : un compilador ML estándar para Common Language Runtime con extensiones para la vinculación con otro código del framework .NET.
  • ML Kit archivado el 7 de enero de 2016 en Wayback Machine : una implementación basada muy fielmente en la Definición, que integra un recolector de basura (que se puede deshabilitar) y gestión de memoria basada en regiones con inferencia automática de regiones, con el objetivo de admitir aplicaciones en tiempo real.

Derivado

Investigación

  • CakeML es una versión REPL de ML con un entorno de ejecución formalmente verificado y traducción a lenguaje ensamblador.
  • Isabelle ( Isabelle/ML, archivado el 30 de agosto de 2020 en Wayback Machine ) integra Poly/ML paralelo en un demostrador de teoremas interactivo, con un IDE sofisticado (basado en jEdit ) para Standard ML oficial (SML'97), el dialecto Isabelle/ML y el lenguaje de demostración. A partir de Isabelle2016, también incluye un depurador a nivel de código fuente para ML.
  • Poplog implementa una versión de Standard ML, junto con Common Lisp y Prolog , lo que permite la programación en lenguajes mixtos; todos están implementados en POP-11 , que se compila de forma incremental .
  • TILT es un compilador certificador completo para Standard ML que utiliza lenguajes intermedios tipados para optimizar el código y garantizar su corrección, y puede compilar a lenguaje ensamblador tipado .

Todas estas implementaciones son de código abierto y de libre acceso. La mayoría están implementadas en Standard ML. Ya no existen implementaciones comerciales; Harlequin , ahora desaparecida, produjo en su momento un IDE y compilador comercial llamado MLWorks, que pasó a formar parte de Xanalys y posteriormente se convirtió en código abierto tras su adquisición por Ravenbrook Limited el 26 de abril de 2013.

Proyectos importantes que utilizan SML

La arquitectura empresarial completa de la Universidad de TI de Copenhague está implementada en aproximadamente 100.000 líneas de SML, incluyendo registros de personal, nómina, administración y retroalimentación de cursos, gestión de proyectos estudiantiles e interfaces de autoservicio basadas en la web. [ 9 ]

Los asistentes de prueba HOL4 , Isabelle , LEGO y Twelf están escritos en Standard ML. También lo utilizan los desarrolladores de compiladores y los diseñadores de circuitos integrados, como ARM . [ 10 ]

Véase también

Referencias

  1. Harper, Robert (5 de mayo de 1998). "Programación en Standard ML" (PDF) . Archivado del original (PDF) el 30 de diciembre de 2025. Recuperado el 30 de diciembre de 2025 .
  2. 1 2 "SML '97" . www.smlnj.org .
  3. "itertools — Funciones que crean iteradores para bucles eficientes — Documentación de Python 3.7.1rc1" . docs.python.org .
  4. "Influencias - The Rust Reference" . The Rust Reference . Consultado el 31 de diciembre de 2023 .
  5. ^ Milner, Robin ; Tofte, Mads ; Harper, Robert (1990). La definición de ML estándar (PDF) . Cambridge, Massachusetts: Prensa del MIT. ISBN 978-0-262-13255-8Archivado del original (PDF) el 8 de octubre de 2025.
  6. ^ Milner, Robin; Tofte, Mads; Harper, Robert; MacQueen, David (1997). La definición de ML estándar (revisada) (PDF) . Prensa del MIT. ISBN 978-0-262-63181-5Archivado del original (PDF) el 23 de noviembre de 2025.
  7. Okasaki, Chris (1 de septiembre de 2000). "Numeración en amplitud: lecciones de un pequeño ejercicio de diseño de algoritmos" . ACM SIGPLAN Notices . 35 (9): 131– 136. doi : 10.1145/357766.351253 . ISSN 0362-1340 . 
  8. "Biblioteca base estándar de ML" . smlfamily.github.io . Consultado el 10 de enero de 2022 .
  9. Tofte, Mads (2009). "Lenguaje ML estándar" . Scholarpedia . 4 (2): 7515. Bibcode : 2009SchpJ...4.7515T . doi : 10.4249/scholarpedia.7515 .
  10. Alglave, Jade ; Fox, Anthony CJ; Ishtiaq, Samin; Myreen, Magnus O.; Sarkar, Susmit; Sewell, Peter; Nardelli, Francesco Zappa (2009). La semántica de la potencia y el código máquina del multiprocesador ARM (PDF) . DAMP 2009. págs. 13–24 . doi : 10.1145/1481839.1481842 . Archivado (PDF) del original el 14 de agosto de 2017. 

Acerca del aprendizaje automático estándar

  • Definición revisada
  • Proyecto Standard ML Family en GitHub archivado el 20 de febrero de 2020 en Wayback Machine .
  • ¿Qué es SML?
  • ¿Qué es SML '97?

Acerca del sucesor ML

  • ML sucesor (sML) : evolución del ML utilizando el ML estándar como punto de partida.
  • HaMLet en GitHub : implementación de referencia para ML sucesor

Práctico

  • Tutorial introductorio básico
  • Ejemplos en código Rosetta

Académico

  • Programación en Standard ML
  • Programación en Standard ML '97: Un tutorial en línea