Standard ML

Standard ML (SML) je staticky orientovaný typově bezpečný univerzální programovací jazyk, který ztělesňuje mnoho nápadů v oblasti návrhů a implementace programovacích jazyků. Podporuje polymorfní inference typů a automaticky zajišťuje efektivní správu paměti. Podporuje funkcionální programování, ale zároveň umožňuje imperativní programování. Usnadňuje programování s rekurzí a symbolických datových struktur skrze podporu šablon (angl. „pattern matching“). Tento jazyk je vybaven rozšiřitelným mechanismem pro manipulaci výjimek a poskytuje flexibilní modulovací prostředky pro strukturování velkých programů. Většina implementací nabízí rozsáhlé knihovny a užitečné vývojové nástroje. Téměř všechny kompilátory generují nativní strojový kód, a to i při interaktivním režimu interpreta. Jazyk je populární mezi informatiky, kteří kompilátory navrhují a nebo implementují.

SML je moderní potomek programovacího jazyka ML, který byl poprvé navržen k práci na projektu „Logic for Computable Functions (LCF)“ k dokazování teorémů. Zkratka "ML" je odvozená z anglického „Meta Language“, což přeloženo do češtiny znamená „meta jazyk“ a je výrazem z oblasti jazykové logiky. V němž jsou analyzovány i jiné jazyky (formální nebo neformální). V roce 1983, Milner[1][2] napsal první návrh standardizované formy ML a během následujících tří let se vyvinul jazyk Standard ML.

Ukázky kódu

Standardní ML je standardizovaný funkcionální programovací jazyk s některými „nečistými“ rysy (angl. „impure features“), které umožňuji vedlejší účinky (angl. „side effects“). Jako všechny funkcionální jazyky, klíčovým a základním prvkem Standard ML je funkce. Zde je příklad faktoriální funkce:

fun factorial n = 
       if n = 0 then 1 else n * factorial (n-1)

Kompilátor Standard ML umí odvodit statický typ int -> int pro tuto funkci. Nejdříve odvodí, že „n“ je pouze uvedená s celočíselnými výrazy (int), a z toho odvodí, že výsledek výpočtu musí být také celé číslo. Stejná funkce může být vyjádřena pomocí klausule. „If-then-else“ nahradíme sekvencí šablon oddělených symbolem "|", kde každý prvek v seznamu je vyzkoušen jeden po druhém, dokud není nalezena shoda:

fun factorial 0 = 1
       | factorial n = n * factorial (n - 1)

Můžeme to také napsat takto:

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

Nebo také jako lambda:

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

Tři klíčová slova:

  • „val“ zavádí vazbu (binding),
  • „fn“ zavádí definici anonymní funkce,
  • „case“ představuje sekvenci vzorů a odpovídajících výrazů. Použitím lokální funkce, výše uvedenou funkci můžeme přepsat na účinnější styl rekurze, tzv. „tail recursion“:
fun factorial n = let
   fun lp (0, acc) = acc
     | lp (m, acc) = lp (m-1, m*acc)
   in
       lp (n, 1)
   end

Standard ML poskytuje silnou podporu pro algebraické datové typy. Datový typ v SML může být chápán jako disjunktní spojení prvku. Jsou snadno definovatelné a snadno se s nimi programuje hlavně proto, že SML dokáže dobře pracovat se vzory (pattern matching) a kontrolou úplnosti vzorů i kontrolou redundance vzorů. Typ je definován pomocí klíčového slova „type“. Taková entita je jen pomůcka programátora, tedy synonymem, které pomáhá vytvářet abstrakci. Zde je například typ pro bod v rovině:

 type bod = real * real

Datový typ je definován pomoci klíčového slova „datatype“:

datatype Shape
  = Circle   of loc * real (* center and radius *)
  | Square   of loc * real (* upper-left corner and side length *)

Vzory lze vložit do definice funkce takto:

fun area (Circle (_, r)) = 3.14 * r * r
  | area (Square (_, s)) = s * s

Všimněte si, že dílčí části, jejichž hodnoty nejsou nutné v tomto výpočtu, nahrazujeme podtržítkem zastupujícím tomto případě vzor.

Funkce v SML jsou vyššího řádu a mohou přijímat jiné funkce jako své argumenty:

fun applyToBoth f x y = (f x, f y)

Funkce mohou produkovat další funkce a vracet je:

fun constantFn k = 
   let
     fun const anything = k
   in
    const
   end

případně:

fun constantFn k = (fn anything => k)

Funkce mohou přijímat i produkovat funkce:

fun compose (f, g) = 
    let
       fun h x = f (g x)
    in
       h
    end

případně:

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

Výjimky

Výjimky jsou vyvolány pomoci klíčovým slovem „raise“, a zachází se s nimi pomoci vzorů.

exception Undefined

fun max [x] = x
  | max (x::xs) = let val m = max xs in if x > m then x else m end
  | max [] = raise Undefined
fun main xs = let
   val msg = (Int.toString (max xs)) handle Undefined => "empty list, no max!"
  in
    print (msg ^ "\n")
  end

Výjimky lze i využit k realizaci výstupu z funkce:

exception Zero

fun listProd ns = let
   fun p [] = 1
       | p (0::_) = raise Zero
       | p (h::t) = h * p t
  in
    (p ns) handle Zero => 0
  End

Když výjimka „Zero“ je vybrána pro vzor odpovídající případu 0, funkci opustíme úplně.

Výrokový jazyk

Všimněte si relativní snadnosti, se kterou je zde definován a zpracováván malý výrokový jazyk:

exception Err

datatype ty
  = IntTy
  | BoolTy
datatype exp
  = True
  | False
  | Int of int
  | Not of exp
  | Add of exp * exp
  | If of exp * exp * exp
fun typeOf (True) = BoolTy
  | typeOf (False) = BoolTy
   | typeOf (Int _) = IntTy
   | typeOf (Not e) = if typeOf e = BoolTy then BoolTy else raise Err
  | typeOf (Add (e1, e2)) = 
      if (typeOf e1 = IntTy) andalso (typeOf e2 = IntTy) then IntTy else raise Err
  | typeOf (If (e1, e2, e3)) = 
      if typeOf e1 <> BoolTy then raise Err
      else if typeOf e2 <> typeOf e3 then raise Err
      else typeOf e2
fun eval (True) = True
  | eval (False) = False
   | eval (Int n) = Int n
   | eval (Not e) = 
     (case eval e
        of True => False
         | False => True
          | _ => raise Fail "type-checking is broken")
  | eval (Add (e1, e2)) = let
       val (Int n1) = eval e1
      val (Int n2) = eval e2
      in
        Int (n1 + n2)
      end
  | eval (If (e1, e2, e3)) = 
      if eval e1 = True then eval e2 else eval e3
fun chkEval e = (ignore (typeOf e); eval e) (* will raise Err on type error *)

Implementace

Existuji mnoha implementace SML:

Reference

  1. Robin Milner. How ML evolved. Polymorphism: The ML/LCF/Hope Newsletter, 1(1), 1983.
  2. Robin Milner. Changes to the Standard ML core language. Technical Report ECS-LFCS-87-33, Laboratory for Foundations of Computer Science, Edinburgh University, 1987