LunarML: definice vlastních operátorů a pokročilý typový systém

16. 6. 2026
Doba čtení: 54 minut

Sdílet

LunarML
Autor: Root.cz s využitím Duck.ai
Popíšeme si a vyzkoušíme dvě možná nejdůležitější vlastnosti jazyka Standard ML: pokročilou možnost definice vlastních operátorů s volitelnou asociativitou i prioritou a taktéž definici rekurzivních datových typů.

Vestavěné operátory programovacího jazyka Standard ML

Co se dozvíte v článku
  1. Vestavěné operátory programovacího jazyka Standard ML
  2. Zápis standardních operátorů
  3. Přetěžování operátorů
  4. Definice vlastních operátorů
  5. Volba asociativity operátorů
  6. Nový operátor s nízkou prioritou
  7. Zápis operátoru speciálními znaky
  8. Strukturované uživatelské datové typy: od jednoduchých výčtových typů po rekurzivní datové struktury
  9. Definice výčtového typu s osmi základními barvami
  10. Definice nového unifikovaného typu color a funkce pro převod barvy na trojici složek RGB
  11. Přidání možnosti modifikace základních barev o atribut světlost
  12. Přidání podpory pro reprezentaci barvy ve stupních šedi
  13. Zjednodušení zápisu funkce color_as_string s využitím nového operátoru kolony
  14. Poslední typ barvy: mix dvou barev (rekurzivní datový typ)
  15. Transpřeklad poslední varianty colors6.sml do jazyka Lua a JavaScriptu
  16. Příloha: Makefile pro transpřeklad všech demonstračních příkladů
  17. Repositář s demonstračními příklady
  18. Literatura
  19. Předchozí články o jazyku ML/SML i o jazycích OCaml a F#
  20. Odkazy na Internetu

Vzhledem k tomu, že překladač (resp. přesněji řečeno transpřekladač) LunarML, kterým se v této sérií článků zabýváme, je jednou z implementací programovacího jazyka Standard ML, bude možné dnešní článek založit právě na specifikaci Standard ML (což je mimochodem jeden z mála mainstreamových programovacích jazyků, které jsou formálně popsány a navíc i definovány tímto formálním popisem).

V programovacím jazyku Standard ML nalezneme relativně velké množství různých operátorů. Musíme si však uvědomit, že operátory jsou ve skutečnosti definovány jako běžné funkce a samotný zápis operátoru je pouhý syntaktický cukr (ovšem velmi praktický), který spočívá v tom, že se (infixové) operátory zapisují mezi operandy a nepoužívají se závorky okolo operandů (pokud to není nutné z jiných důvodů – například při určení priority atd.). Pokud na chvíli zůstaneme u infixových operátorů, tak to konkrétně znamená, že zápis:

<výraz1> <operátor;> <výraz2>

je ve skutečnosti ze sémantického pohledu totožný se zápisem:

<operátor;>(<výraz1>, <výraz2>)
Poznámka: to ovšem znamená, že se jedná o funkci akceptující jediný parametr typu n-tice (konkrétně dvojice). Je to odlišné od zápisu, který nelze (alespoň ne přímo) převést na infixový operátor:
<operátor;> <výraz1> <výraz2>

V následující tabulce jsou vypsány všechny standardní operátory programovacího jazyka Standard ML. Ty se od sebe liší (pochopitelně) nejenom prováděnou operací, ale taktéž prioritou a asociativitou. Priorita určuje (stejně jako v jiných jazycích), v jakém pořadí se budou operátory vyhodnocovat v případě, že jsou použity v jediném výrazu (například ve výrazu 1+2*3 jsou zapsány dva operátory a pořadí vyhodnocení závisí na jejich prioritě). A asociativita určuje uzávorkování operátorů se stejnou prioritou, tj. ovlivní, zda se provede (1+2)+3 nebo naopak 1+(2+3).

Z níže uvedené tabulky je patrné, že většina operátorů je takzvaně asociativní zleva, ovšem skupina dvou operátorů s prioritou 5 má asociativitu zprava. V tomto případě se konkrétně jedná o operátory určené pro spojení řetězců popř. seznamů, s nimiž jsme se ve stručnosti seznámili v úvodním článku o transpřekladači LunarML:

Operátor Priorita Asociativita Poznámka
* 7 zleva pro int i real
/ 7 zleva operandy musí být typu real
div 7 zleva operandy musí být typu int
mod 7 zleva operandy musí být typu int
       
+ 6 zleva pro int i real
6 zleva pro int i real
^ 6 zleva spojení řetězců
       
:: 5 zprava konstrukce seznamu připojením dalšího prvku
@ 5 zprava spojení seznamů
       
= 4 zleva rovnost
<> 4 zleva nerovnost
> 4 zleva  
>= 4 zleva  
< 4 zleva  
<= 4 zleva  
       
:= 3 zleva přiřazení do měnitelné proměnné
o 3 zleva kompozice funkcí
       
before 0 zleva většinou definovaný v knihovně

Booleovské operace andalso a orelse nejsou v tabulce uvedeny, protože se jedná o speciální konstrukce realizující zkrácené vyhodnocování. Z tohoto důvodu je nelze definovat formou funkcí (u funkcí se totiž parametry vyhodnocují před zavoláním funkce, kdežto v případě andalso a orelse tomu tak být nemusí).

Poznámka: povšimněte si způsobu zápisu operátorů pro test na rovnost a nerovnost, který se liší od mainstreamových programovacích jazyků, jež jsou do značné míry odvozeny od céčka.

Zápis standardních operátorů

Všechny operátory, které byly uvedeny v předchozí kapitole, jsou interně implementovány formou (běžných) funkcí a dále jsou definovány klíčovými slovy infix a v některých případech infixr. Samotná implementace operátorů není tématem dnešního článku, ovšem zajímavý je druhý krok, tedy použití klíčových slov infix a infixr, které vlastně v době překladu mění syntaxi programovacího jazyka:

infix  7  * / div mod
infix  6  + - ^
infixr 5  :: @
infix  4  = <> > >= < <=
infix  3  := o
infix  0  before
Poznámka: povšimněte si, že ne všechny priority jsou ve skutečnosti obsazeny standardními operátory, což pochopitelně není náhoda. V praxi totiž může nastat potřeba vytvořit další operátory s prioritou menší, než mají standardní operátory nebo naopak prioritou větší.

Přetěžování operátorů

V mnoha programovacích jazycích se setkáme s takzvanými přetíženými operátory. Jedná se o operátory, jejichž funkce a vlastnosti se mění podle typu operandů. Pravděpodobně nejznámější formou přetížených operátorů jsou operátory určené pro provádění základních aritmetických operací, které bývají definovány pro různé numerické datové typy. Ovšem například operátor + může být přetížen vícekrát, typicky pro operaci spojení řetězců (viz například Java) či například n-tic a seznamů (Python). V těchto případech se nejenom mění funkce operátoru (tj. součet je zcela rozdílná operace od spojení řetězců), ale i jejich další vlastnosti, v tomto případě například komutativita (a nepřímo taktéž asociativita v případě hodnot s plovoucí řádovou čárkou).

Některé programovací jazyky umožňují definici zcela nových operátorů popř. přetížení existujících operátorů. Z tohoto pohledu můžeme programovací jazyky rozdělit do čtyř skupin na základě dvou vlastností (které jsou na sobě nezávislé):

  1. Lze definovat nové operátory?
  2. Lze přetížit stávající operátory?
Poznámka: existující „původní“ operátory jsou v mnoha programovacích jazycích již přetíženy tvůrci jazyka, což ovšem nic neříká o tom, zda je možné je dále přetížit na úrovni programu nebo knihovny. Dobrým příkladem je například již zmíněná Java, v níž nelze dále přetížit operátor + pro různé třídy, například typu BigDecimal, i když by to bylo sémanticky naprosto vyhovující.

Zajímavé je, že každá ze čtyř kombinací zmíněných v předchozí kapitole je obsazena nějakým programovacím jazykem (a to zcela nezávisle na dalších vlastnostech daného jazyka, tedy nezávisle na typovém systému, podpoře OOP, maker atd.):

Nové operátory nelze přidávat a existující operátory nelze dále přetěžovat:

BASIC
C
Go
Java
JavaScript
Modula-2
Objective-C
Pascal
TypeScript
Visual Basic

Nové operátory nelze přidávat, ale existující operátory je možné přetěžovat:

Ada
C#
C++
D
Dart
FreeBASIC
Groovy
Java
Kotlin
Lua
MATLAB
Object Pascal
PHP
Perl
Python
Ruby
Rust
Visual Basic .NET

Lze definovat nové operátory, existující operátory ovšem nelze dále přetěžovat:

Standard ML (téma dnešního článku)
Pico
Prolog
Smalltalk

Lze definovat nové operátory, navíc je možné přetěžovat existující operátory:

ALGOL 68
Eiffel
Fortran
F#
Haskell
Io
Nim
R
Raku
Scala
Swift

Definice vlastních operátorů

V programovacím jazyku Standard ML sice není (prakticky) možné přetěžovat standardní operátory (viz též předchozí kapitolu), ovšem samotný jazyk nám nezabraňuje ve vytváření operátorů nových. U nových operátorů lze určit jejich prioritu i asociativitu, což je velmi užitečné (podle mého skromného názoru dokonce mnohem užitečnější, než pouhé přetěžování existujících operátorů).

Poznámka: prozatím se budeme zabývat vytvořením operátoru, který má běžné jméno, například pow. Až poté si řekneme a prakticky ukážeme, že jméno operátoru může být složeno i ze speciálních znaků, v našem případě z **.

Prvním krokem při vytváření nového operátoru je definice funkce se dvěma parametry, která bude následně zaregistrována jako nový binární operátor. Můžeme si například vyzkoušet zadefinovat funkci pro umocnění mn. S touto funkcí jsme se již setkali v úvodním článku o LunarML, takže jen ve zkratce:

fun pow(m, 0) = 1
  | pow(m, n) = m * pow(m, n-1);

Tuto funkci následně převedeme na operátor, konkrétně na operátor asociativní zleva s prioritou 8 (priorita je tedy vyšší, než u součinu a podílu, což je dobře):

infix 8 pow;

Nyní je již možné nový operátor začít používat, a to se všemi výhodami, které přináší statický typový systém programovacího jazyka ML:

2 pow 3;
2 pow 3 + 1;
Poznámka: ve skutečnosti asi budete chtít zajistit pravou asociativitu operátoru pow, takže namísto infix použijeme klíčové slovo infixr:
infixr 8 pow;

Vše si ověříme na uceleném příkladu:

(* Nový operátor pow *)
 
fun pow(m, 0) = 1
  | pow(m, n) = m * pow(m, n-1);
 
 
infix 8 pow;
 
val point = 1 pow 1;
val square = 2 pow 2;
val cube = 3 pow 3;
 
print(Int.toString (point));
print "\n";
 
print(Int.toString (square));
print "\n";
 
print(Int.toString (cube));
print "\n";

Výsledky vypsané po překladu a spuštění:

1
4
27

Taktéž se podíváme na výsledek (trans)překlad do jazyka Lua. Převedení je dosti přímočaré:

  local function pow(m, n)
    if n == 0 then
      return 1
    end
    local tmp = pow(m, _Int_sub(n, 1))
    return _Int_mul(m, tmp)
  end
  local point = pow(1, 1)
  local square = pow(2, 2)
  cube = pow(3, 3)
  local tmp = gsub(tostring(point), "-", "~")
  outputAndFlush(stdOut, tmp)
  outputAndFlush(stdOut, "\n")
  local tmp1 = gsub(tostring(square), "-", "~")
  outputAndFlush(stdOut, tmp1)
  outputAndFlush(stdOut, "\n")

Výsledek transpřekladu do JavaScriptu vypadá podobně:

 let pow;
 pow = function(m, n) {
  if (n === 0) {
   return 1;
  } else {
   const tmp16 = pow(m, _Int54_sub(n, 1));
   return _Int54_mul(m, tmp16);
  }
 };
 const point = pow(1, 1);
 const square = pow(2, 2);
 const cube = pow(3, 3);
 const tmp13 = toString(point);
 outputAndFlush(stdOut[0], tmp13);
 outputAndFlush(stdOut[0], Uint8Array.of(10));
 const tmp14 = toString(square);
 outputAndFlush(stdOut[0], tmp14);
 outputAndFlush(stdOut[0], Uint8Array.of(10));
 const tmp15 = toString(cube);
 outputAndFlush(stdOut[0], tmp15);
 outputAndFlush(stdOut[0], Uint8Array.of(10));

Volba asociativity operátorů

V předchozím textu byla vypsána pravidla platná pro nově definované operátory. Mj. jsme se zmínili o asociativitě, tedy o tom, zda bude pro nějaký operátor op zápis:

x op y op z

vyhodnocen takto:

(x op y) op z

či naopak takto:

x op (y op z)

Operátor definovaný klíčovým slovem infix bude mít prioritu zleva:

(* Nový operátor pow *)
 
fun pow(m, 0) = 1
  | pow(m, n) = m * pow(m, n-1);
 
 
infix 8 pow;
 
val result = 2 pow 3 pow 2;
print(Int.toString (result));
print "\n";

Což vede k výpočtu (23)2=82=64:

64

Naproti tomu se operátor s asociativitou zprava definuje klíčovým slovem infixr (s „r“ na konci):

(* Nový operátor pow *)
 
fun pow(m, 0) = 1
  | pow(m, n) = m * pow(m, n-1);
 
 
infixr 8 pow;
 
val result = 2 pow 3 pow 2;
print(Int.toString (result));
print "\n";

Nyní bude proveden výpočet 232=29=512:

512

Transpřeklad první verze do jazyka Lua bude následující:

local function pow(m, n)
  if n == 0 then
    return 1
  end
  local tmp = pow(m, _Int_sub(n, 1))
  return _Int_mul(m, tmp)
end
local tmp = pow(2, 3)
local result = pow(tmp, 2)

Transpřeklad první verze do JavaScriptu:

 pow = function(m, n) {
  if (n === 0) {
   return 1;
  } else {
   const tmp15 = pow(m, _Int54_sub(n, 1));
   return _Int54_mul(m, tmp15);
  }
 };
 const tmp13 = pow(2, 3);
 const result = pow(tmp13, 2);

Transpřeklad druhé verze do jazyka Lua bude odlišný:

local function pow(m, n)
  if n == 0 then
    return 1
  end
  local tmp = pow(m, _Int_sub(n, 1))
  return _Int_mul(m, tmp)
end
local tmp = pow(3, 2)
local result = pow(2, tmp)

Totéž platí i pro transpřeklad do JavaScriptu:

 pow = function(m, n) {
  if (n === 0) {
   return 1;
  } else {
   const tmp15 = pow(m, _Int54_sub(n, 1));
   return _Int54_mul(m, tmp15);
  }
 };
 const tmp13 = pow(3, 2);
 const result = pow(2, tmp13);

Nový operátor s nízkou prioritou

Podívejme se nyní na ukázku dalšího uživatelsky definovaného operátoru. Užitečný může být operátor nazvaný exists, který bude používán pro detekci, zda seznam obsahuje určitý prvek či nikoli (lepší by bylo operátor pojmenovat in, toto klíčové slovo je však již obsazeno). Vlastní implementace funkce exists je relativně snadná:

fun exists(element, []) = false
  | exists(element, head::tail) = if element = head then true
                                  else exists(element, tail);

Funkci (a jedná se o běžnou funkci) si samozřejmě můžeme otestovat:

exists(2, [1,2,3]);
exists(0, [1,2,3]);
exists(42, [42]);
exists(42, []);

A následně lze funkci převést na operátor, a to konkrétně operátor s druhou nejnižší prioritou (nejnižší priorita je nastavena na nulu):

infix 1 exists;

Nyní se bude test existence prvku v seznamu zapisovat skutečně formou operátoru:

2 exists [1,2,3];
0 exists [1,2,3];
42 exists [42];
42 exists [];

Sami si vyzkoušejte, jaký vliv má druhá nejnižší priorita (1) na tyto výrazy:

1+1 exists [1,2,3];
3*1 exists [1,2,3];
1+1 exists [1,2] @ [3,4];

Otestování chování v reálném příkladu:

(* Operátor exists *)
 
fun exists(element, []) = false
  | exists(element, head::tail) = if element = head then true
                                  else exists(element, tail);
 
 
infix 1 exists;
 
 
fun bool_to_string true  = "true"
  | bool_to_string false = "false";
 
 
fun print_bool b = print (bool_to_string b ^ "\n");
 
 
print_bool(1+1 exists [1,2,3]);
print "\n";
 
print_bool(3*1 exists [1,2,3]);
print "\n";
 
print_bool(1+1 exists [1,2] @ [3,4]);
print "\n";
Poznámka: využíváme zde faktu, že exists má velmi nízkou prioritu a proto není nutné zapisovat závorky.

Po spuštění by se měla zobrazit trojice řádků:

true
true
true

Opět se podíváme na výsledek (trans)překlad do jazyka Lua. Převod je dosti přímočarý:

  local function exists(eq1)
    return function(a)
      if a[2] == nil then
        return false
      elseif a[2] ~= nil then
        local element = a[1]
        local tmp1 = a[2][1]
        local tmp2 = a[2][2]
        local tmp3 = eq1(element, tmp1)
        if tmp3 then
          return true
        end
        local tmp4 = exists(eq1)
        return tmp4({element, tmp2})
      else
        _raise(_Match, "exists.sml:3:5")
      end
    end
  end

Výsledek transpřekladu do JavaScriptu:

 exists = function(eq1) {
  return _wrap(function(a) {
   if (a[1] === null) {
    return [true, false];
   } else if (! (a[1] === null)) {
    const element = a[0];
    const tmp22 = a[1][0];
    const tmp23 = a[1][1];
    const tmp24 = eq1(element, tmp22);
    if (tmp24) {
     return [true, true];
    } else {
     const tmp25 = exists(eq1);
     return [false, tmp25, [[element, tmp23]]];
    }
   } else {
    throw _Match;
   }
  });

Zápis operátoru speciálními znaky

Náš nový operátor pro umocnění byl prozatím pojmenován pow, což nevypadá jako běžný operátor. Ovšem jazyk Standard ML umožňuje použít pro zápis operátoru i některé speciální znaky. V praxi to tedy znamená, že například umocnění můžeme zapsat dvojicí znaků ** (stříška je již zabrána), takže si předchozí příklady zopakujme s novými jmény:

(* Nový operátor ** *)
 
fun **(m, 0) = 1
  | **(m, n) = m * **(m, n-1);
 
 
infix 8 **;
 
val point = 1 ** 1;
val square = 2 ** 2;
val cube = 3 ** 3;
 
print(Int.toString (point));
print "\n";
 
print(Int.toString (square));
print "\n";
 
print(Int.toString (cube));
print "\n";
(* Nový operátor ** *)
 
fun **(m, 0) = 1
  | **(m, n) = m * **(m, n-1);
 
 
infix 8 **;
 
val result = 2 ** 3 ** 2;
print(Int.toString (result));
print "\n";
(* Nový operátor ** *)
 
fun **(m, 0) = 1
  | **(m, n) = m * **(m, n-1);
 
 
infixr 8 **;
 
val result = 2 ** 3 ** 2;
print(Int.toString (result));
print "\n";

Jen v rychlosti se podívejme i na způsob transpřekladu, kde se musí (kvůli omezení jazyků Lua i JavaScript) identifikátor funkce s realizací operátoru pojmenovat generovaným jménem:

  local function _ASTER_ASTER(m, n)
    if n == 0 then
      return 1
    end
    local tmp = _ASTER_ASTER(m, _Int_sub(n, 1))
    return _Int_mul(m, tmp)
  end
  local point = _ASTER_ASTER(1, 1)
  local square = _ASTER_ASTER(2, 2)
  cube = _ASTER_ASTER(3, 3)
 let $ASTER$ASTER;
 $ASTER$ASTER = function(m, n) {
  if (n === 0) {
   return 1;
  } else {
   const tmp16 = $ASTER$ASTER(m, _Int54_sub(n, 1));
   return _Int54_mul(m, tmp16);
  }
 };
 const point = $ASTER$ASTER(1, 1);
 const square = $ASTER$ASTER(2, 2);
 const cube = $ASTER$ASTER(3, 3);

Strukturované uživatelské datové typy: od jednoduchých výčtových typů po rekurzivní datové struktury

Ve druhé části dnešního článku se ještě jednou vrátíme k typovému systému tohoto zajímavého (a možná poněkud přehlíženého) programovacího jazyka. Ukážeme si, jakým způsobem lze realizovat datový typ pro reprezentaci barvy, a to v různých barvových modelech. Navíc bude podporována i definice barvy, která vznikne smícháním dvou jiných barev, což vlastně v důsledku musí znamenat, že datový typ je rekurzivní. Řešení, které využijeme, bude založeno na „neobjektovém“ přístupu, protože objekty v klasickém slova smyslu v jazyku Standard ML nenajdeme (ovšem například ideově velmi blízký jazyk OCaml práci s nimi podporuje). Ukážeme a zopakujeme si jak vlastnosti typového systému, tak i pattern matching (s jeho možným zkráceným zápisem).

Poznámka: rekurzivní datové typy ve skutečnosti nejsou v praxi žádnou výjimkou a setkáme se s nimi prakticky všude. Ovšem vzhledem k tomu, že v dalších jazycích nebývají přímo podporovány, je jejich použití (uměle) omezováno, popř. se musí používat triky typu „ukazatel na hodnotu typu“. Typickým příkladem rekurzivního typu je jakýkoli popis hierarchie. Sice poněkud předbíháme, ale takto bude vypadat typově silná definice binárního stromu (jde o generický typ – viz použití 'a neboli alfa):
tree = datatype 'a tree = Node of {data:'a, left:'a tree, right:'a tree}
                 | Leaf

Definice výčtového typu s osmi základními barvami

Popis typového systému Standard ML začneme datovým typem, který bychom již měli dobře znát z běžných programovacích jazyků, i když pod jiným jménem. Jedná se o takzvané disjunktní zobrazení, které obsahuje pouze výčet jmen. Tento datový typ použijeme pro reprezentaci základních osmi barev v barvové paletě:

datatype basic_color =
    Black
  | Red
  | Green
  | Yellow
  | Blue
  | Magenta
  | Cyan
  | White
;
Poznámka: povšimněte si způsobu zápisu s použitím znaků |. Velmi podobný zápis se používá i u pattern matchingu, což není náhoda.

Na rozdíl od jazyka OCaml se před název první barvy znak | nezapisuje, protože náš víceřádkový zápis vlastně znamená:

datatype basic_color = Black | Red | Green | Yellow | Blue | Magenta | Cyan | White ;

Jen na okraj: v jazyku OCaml vypadá definice disjunktního zobrazení nepatrně odlišně:

type basic_color =
  | Black
  | Red
  | Green
  | Yellow
  | Blue
  | Magenta
  | Cyan
  | White
;;

Definice nového unifikovaného typu color a funkce pro převod barvy na trojici složek RGB

V dalším textu budeme postupně budovat datový typ, který bude možné využít pro uložení barvy v jakékoli reprezentaci. První verze tohoto typu bude obsahovat jen jedinou možnost – již výše definovaný typ basic_color:

datatype color =
    BasicColor of basic_color;

Postupně budeme přidávat i další možnosti.

Dále se pokusme definovat funkci, která převede barvu ze základní palety osmi barev na tři složky RGB. Tyto složky můžeme reprezentovat trojicí celočíselných hodnot v rozsahu 0 až 255 a samotný převod lze realizovat funkcí (nazvěme ji basic_color_to_rgb) s pattern matchingem. Realizace takové funkce je sice poněkud naivní, ale zcela korektní. Spočívá ve výčtu všech možných způsobů volání této funkce. Žádný jiný způsob volání této funkce možný není – to za nás ověří překladač (tudíž nikdy nevznikne výjimka typu „špatný parametr“):

fun basic_color_to_rgb Black   = (0,   0,   0)
  | basic_color_to_rgb Red     = (255, 0,   0)
  | basic_color_to_rgb Green   = (0,   255, 0)
  | basic_color_to_rgb Yellow  = (255, 255, 0)
  | basic_color_to_rgb Blue    = (0,   0,   255)
  | basic_color_to_rgb Magenta = (255, 0,   255)
  | basic_color_to_rgb Cyan    = (0,   255, 255)
  | basic_color_to_rgb White   = (255, 255, 255)
  ;

Pro otestování funkcionality nového typu příklad doplníme o pomocné funkce provedeme konstrukci barev s výpisem výsledků:

datatype basic_color =
    Black
  | Red
  | Green
  | Yellow
  | Blue
  | Magenta
  | Cyan
  | White
;
 
 
datatype color =
    BasicColor of basic_color;
 
 
fun basic_color_to_rgb Black   = (0,   0,   0)
  | basic_color_to_rgb Red     = (255, 0,   0)
  | basic_color_to_rgb Green   = (0,   255, 0)
  | basic_color_to_rgb Yellow  = (255, 255, 0)
  | basic_color_to_rgb Blue    = (0,   0,   255)
  | basic_color_to_rgb Magenta = (255, 0,   255)
  | basic_color_to_rgb Cyan    = (0,   255, 255)
  | basic_color_to_rgb White   = (255, 255, 255)
  ;
 
 
fun i2s integer = Int.toString(integer);
 
 
fun color_as_string (BasicColor c) =
    let val (r, g, b) = basic_color_to_rgb c
    in "red=" ^ i2s(r) ^ ", green=" ^ i2s(g) ^ ", blue=" ^ i2s(b) ^ "\n"
    end
;
 
 
val c1 = BasicColor(Black);
print(color_as_string c1);
 
val c2 = BasicColor(Yellow);
print(color_as_string c2);
 
val c3 = BasicColor(White);
print(color_as_string c3);

Výsledky ukazují, že vše je (prozatím) implementováno korektně:

red=0, green=0, blue=0
red=255, green=255, blue=0
red=255, green=255, blue=255

Způsob překladu funkce color_as_string do jazyka Lua je přímočarý:

  color_as_string = function(a)
    local c = a
    local r, g, b
    if c == "Black" then
      b = 0
      g = 0
      r = 0
    elseif c == "Red" then
      b = 0
      g = 0
      r = 255
    elseif c == "Green" then
      b = 0
      g = 255
      r = 0
    elseif c == "Yellow" then
      b = 0
      g = 255
      r = 255
    elseif c == "Blue" then
      b = 255
      g = 0
      r = 0
    elseif c == "Magenta" then
      b = 255
      g = 0
      r = 255
    elseif c == "Cyan" then
      b = 255
      g = 255
      r = 0
    elseif c == "White" then
      b = 255
      g = 255
      r = 255
    else
      _raise(_Match, "colors_2.sml:17:5")
    end
    local tmp = gsub(tostring(r), "-", "~")
    local tmp1 = "red=" .. tmp .. ", green="
    local tmp2 = gsub(tostring(g), "-", "~")
    local tmp3 = tmp1 .. tmp2 .. ", blue="
    local tmp4 = gsub(tostring(b), "-", "~")
    return tmp3 .. tmp4 .. "\n"
  end

Překlad do JavaScriptu je prakticky totožný, pouze se zde využívá vícenásobné přiřazení:

 const color_as_string = function(a) {
  const c = a;
  let r, g, b;
  if (c === "Black") {
   [r, g, b] = [0, 0, 0];
  } else if (c === "Red") {
   [r, g, b] = [255, 0, 0];
  } else if (c === "Green") {
   [r, g, b] = [0, 255, 0];
  } else if (c === "Yellow") {
   [r, g, b] = [255, 255, 0];
  } else if (c === "Blue") {
   [r, g, b] = [0, 0, 255];
  } else if (c === "Magenta") {
   [r, g, b] = [255, 0, 255];
  } else if (c === "Cyan") {
   [r, g, b] = [0, 255, 255];
  } else if (c === "White") {
   [r, g, b] = [255, 255, 255];
  } else {
   throw _Match;
  }
  const tmp16 = toString(r);
  const tmp17 = _String_append(_String_append(Uint8Array.of(114, 101, 100, 61), tmp16), Uint8Array.of(44, 32, 103, 114, 101, 101, 110, 61));
  const tmp18 = toString(g);
  const tmp19 = _String_append(_String_append(tmp17, tmp18), Uint8Array.of(44, 32, 98, 108, 117, 101, 61));
  const tmp20 = toString(b);
  return _String_append(_String_append(tmp19, tmp20), Uint8Array.of(10));
 };

Přidání možnosti modifikace základních barev o atribut světlost

V navazujícím kroku provedeme další rozšíření našeho systému barev. Přidáme možnost určit u základních osmi barev jejich světlost, tj. atribut Dark či Bright. Typ tohoto atributu je triviální a lze ho zapsat opět s využitím disjunktního zobrazení:

datatype brightness =
    Dark
  | Bright
;

Důležitější ovšem je, že se změní i konstruktor pro základní barvy. Kromě jména barvy (Red atd.) se musí specifikovat i její světlost, takže výsledkem je (typově bezpečná) dvojice:

datatype color =
    BasicColor of basic_color * brightness;
Poznámka: připomeňme si, že zápis hvězdičky („násobení“) naznačuje, jakou mírou se zvyšuje počet všech možných kombinací barva×světlost.

A pochopitelně musíme modifikovat i přepočet základních barev na RGB. Pokud má být základní barva světlá, neprovedeme žádnou změnu, ale pokud má být tmavá, podělíme všechny její barvové složky dvěma (to pro jednoduchost – v praxi se spíše násobí konstantou 2/3):

fun darker (r,g,b) =
  (r div 2, g div 2, b div 2)
;
 
 
fun brightness rgb Dark   = darker rgb
  | brightness rgb Bright = rgb
;

Výpočet si ověříme:

datatype basic_color =
    Black
  | Red
  | Green
  | Yellow
  | Blue
  | Magenta
  | Cyan
  | White
;
 
 
datatype brightness =
    Dark
  | Bright
;
 
 
datatype color =
    BasicColor of basic_color * brightness;
 
 
fun darker (r,g,b) =
  (r div 2, g div 2, b div 2)
;
 
 
fun brightness rgb Dark   = darker rgb
  | brightness rgb Bright = rgb
;
 
 
fun basic_color_to_rgb Black   = (0,   0,   0)
  | basic_color_to_rgb Red     = (255, 0,   0)
  | basic_color_to_rgb Green   = (0,   255, 0)
  | basic_color_to_rgb Yellow  = (255, 255, 0)
  | basic_color_to_rgb Blue    = (0,   0,   255)
  | basic_color_to_rgb Magenta = (255, 0,   255)
  | basic_color_to_rgb Cyan    = (0,   255, 255)
  | basic_color_to_rgb White   = (255, 255, 255)
  ;
 
 
fun i2s integer = Int.toString integer;
 
fun rgb_as_string r g b =
    "red=" ^ i2s(r) ^ ", green=" ^ i2s(g) ^ ", blue=" ^ i2s(b) ^ "\n"
;
 
fun color_as_string (BasicColor c) =
    let val (color, brightness_level) = c
        val rgb = basic_color_to_rgb color
        val (r, g, b) = brightness rgb brightness_level
    in rgb_as_string r g b
    end
;
 
 
val c1 = BasicColor(Black, Dark);
print(color_as_string c1);
 
val c2 = BasicColor(Black, Bright);
print(color_as_string c2);
 
val c3 = BasicColor(Yellow, Dark);
print(color_as_string c3);
 
val c4 = BasicColor(Yellow, Bright);
print(color_as_string c4);
 
val c5 = BasicColor(White, Dark);
print(color_as_string c5);
 
val c6 = BasicColor(White, Bright);
print(color_as_string c6);

Ověření spuštěním tohoto příkladu:

red=0, green=0, blue=0
red=0, green=0, blue=0
red=127, green=127, blue=0
red=255, green=255, blue=0
red=127, green=127, blue=127
red=255, green=255, blue=255

Přidání podpory pro reprezentaci barvy ve stupních šedi

Podporu barev rozšíříme o možnost reprezentace barvy ve stupních šedi. Pro jednoduchost budeme předpokládat, že taková barva bude specifikována jednou celočíselnou hodnotou v rozsahu 0..255 (prozatím nebudeme kontrolovat meze). Datový typ „barva“ tedy bude muset být rozšířen o další pojmenovaný prvek:

datatype color =
    BasicColor of basic_color * brightness
  | Gray of int
  | RGB of int * int * int
;

A rozšířit budeme muset i funkci color_as_string pro převod barvy na trojici složek RGB. Pro jednoduchost bude převod ze stupňů šedi realizován tak, že se vstupní hodnota rozkopíruje do všech třech složek RGB (což ale nemusí být přesné – záleží na konkrétním barvovém prostoru):

fun color_as_string (BasicColor c) =
    let val (color, brightness_level) = c
        val rgb = basic_color_to_rgb color
        val (r, g, b) = brightness rgb brightness_level
    in rgb_as_string r g b
    end
  | color_as_string (Gray(g)) = rgb_as_string g g g
  | color_as_string (RGB (r,g,b)) = rgb_as_string r g b
;

Zdrojový kód takto upraveného programu se sice prodlouží, ale jak definice datového typu color, tak i pomocných funkcí je stále velmi přehledná:

datatype basic_color =
    Black
  | Red
  | Green
  | Yellow
  | Blue
  | Magenta
  | Cyan
  | White
;
 
 
datatype brightness =
    Dark
  | Bright
;
 
 
datatype color =
    BasicColor of basic_color * brightness
  | Gray of int
  | RGB of int * int * int
;
 
 
fun darker (r,g,b) =
  (r div 2, g div 2, b div 2)
;
 
 
fun brightness rgb Dark   = darker rgb
  | brightness rgb Bright = rgb
;
 
 
fun basic_color_to_rgb Black   = (0,   0,   0)
  | basic_color_to_rgb Red     = (255, 0,   0)
  | basic_color_to_rgb Green   = (0,   255, 0)
  | basic_color_to_rgb Yellow  = (255, 255, 0)
  | basic_color_to_rgb Blue    = (0,   0,   255)
  | basic_color_to_rgb Magenta = (255, 0,   255)
  | basic_color_to_rgb Cyan    = (0,   255, 255)
  | basic_color_to_rgb White   = (255, 255, 255)
  ;
 
 
fun i2s integer = Int.toString integer;
 
fun rgb_as_string r g b =
    "red=" ^ i2s(r) ^ ", green=" ^ i2s(g) ^ ", blue=" ^ i2s(b) ^ "\n"
;
 
fun color_as_string (BasicColor c) =
    let val (color, brightness_level) = c
        val rgb = basic_color_to_rgb color
        val (r, g, b) = brightness rgb brightness_level
    in rgb_as_string r g b
    end
  | color_as_string (Gray(g)) = rgb_as_string g g g
  | color_as_string (RGB (r,g,b)) = rgb_as_string r g b
;
 
 
val c1 = BasicColor(Black, Dark);
print(color_as_string c1);
 
val c2 = BasicColor(Black, Bright);
print(color_as_string c2);
 
val c3 = BasicColor(Yellow, Dark);
print(color_as_string c3);
 
val c4 = BasicColor(Yellow, Bright);
print(color_as_string c4);
 
val c5 = BasicColor(White, Dark);
print(color_as_string c5);
 
val c6 = BasicColor(White, Bright);
print(color_as_string c6);
 
val c7 = Gray 128;
print(color_as_string c7);
 
val c8 = Gray 255;
print(color_as_string c8);
 
val c9 = RGB(10, 20, 30);
print(color_as_string c9);

Ověření funkcionality:

red=0, green=0, blue=0
red=0, green=0, blue=0
red=127, green=127, blue=0
red=255, green=255, blue=0
red=127, green=127, blue=127
red=255, green=255, blue=255
red=128, green=128, blue=128
red=255, green=255, blue=255
red=10, green=20, blue=30

Zjednodušení zápisu funkce color_as_string s využitím nového operátoru kolony

Funkce color_as_string je ve variantě použité v předchozím demonstračním příkladu sice použitelná a korektní, ale interně je příliš komplikovaná (minimálně z pohledu vývojářů používajících jazyky z větve ML):

fun color_as_string (BasicColor c) =
    let val (color, brightness_level) = c
        val rgb = basic_color_to_rgb color
        val (r, g, b) = brightness rgb brightness_level
    in rgb_as_string r g b
    end
  | color_as_string (Gray(g)) = rgb_as_string g g g
  | color_as_string (RGB (r,g,b)) = rgb_as_string r g b
;

Ovšem funkci můžeme zjednodušit, a to poměrně elegantním způsobem. Nejdříve si nadefinujeme nový operátor, konkrétně operátor realizující kolonu (pipe). Bude se jednat o infixový operátor s nejnižší prioritou (ušetříme za závorky), který realizuje resp. umožňuje zřetězení funkcí. Levým operandem je parametru, pravým funkce. Operátor bude zapisován dvojicí znaků |>:

infix 0 |>;
fun x |> f = f x;

Funkce color_as_string se zkrátí na jediný řádek, který převede barvu na trojici složek RGB a následně tuto trojici převede na řetězec:

fun color_as_string c =
    c |> to_rgb |> rgb_as_string
;

Tím se celý převod stal čitelnější, ovšem zavádí novou funkci to_rgb. To je v pořádku; jedná se o rozložení problému na menší, snadno popsatelné části (mimochodem, i zde se používá pattern matching):

fun to_rgb (BasicColor c) : int * int * int =
    let val (color, brightness_level) = c
        val rgb = basic_color_to_rgb color
    in brightness rgb brightness_level
    end
  | to_rgb (Gray(g)) = (g,g,g)
  | to_rgb (RGB(r,g,b)) = (r,g,b)
;

Upravená varianta demonstračního příkladu bude vypadat následovně:

datatype basic_color =
    Black
  | Red
  | Green
  | Yellow
  | Blue
  | Magenta
  | Cyan
  | White
;
 
 
datatype brightness =
    Dark
  | Bright
;
 
 
datatype color =
    BasicColor of basic_color * brightness
  | Gray of int
  | RGB of int * int * int
;
 
infix 0 |>
fun x |> f = f x;
 
 
fun darker (r,g,b) =
  (r div 2, g div 2, b div 2)
;
 
 
fun brightness rgb Dark   = darker rgb
  | brightness rgb Bright = rgb
;
 
 
fun basic_color_to_rgb Black   = (0,   0,   0)
  | basic_color_to_rgb Red     = (255, 0,   0)
  | basic_color_to_rgb Green   = (0,   255, 0)
  | basic_color_to_rgb Yellow  = (255, 255, 0)
  | basic_color_to_rgb Blue    = (0,   0,   255)
  | basic_color_to_rgb Magenta = (255, 0,   255)
  | basic_color_to_rgb Cyan    = (0,   255, 255)
  | basic_color_to_rgb White   = (255, 255, 255)
  ;
 
 
fun i2s integer = Int.toString integer;
 
fun rgb_as_string (r, g, b) =
    "red=" ^ i2s(r) ^ ", green=" ^ i2s(g) ^ ", blue=" ^ i2s(b) ^ "\n"
;
 
 
fun to_rgb (BasicColor c) : int * int * int =
    let val (color, brightness_level) = c
        val rgb = basic_color_to_rgb color
    in brightness rgb brightness_level
    end
  | to_rgb (Gray(g)) = (g,g,g)
  | to_rgb (RGB(r,g,b)) = (r,g,b)
;
 
 
fun color_as_string c =
    c |> to_rgb |> rgb_as_string
;
 
 
val c1 = BasicColor(Black, Dark);
print(color_as_string c1);
 
val c2 = BasicColor(Black, Bright);
print(color_as_string c2);
 
val c3 = BasicColor(Yellow, Dark);
print(color_as_string c3);
 
val c4 = BasicColor(Yellow, Bright);
print(color_as_string c4);
 
val c5 = BasicColor(White, Dark);
print(color_as_string c5);
 
val c6 = BasicColor(White, Bright);
print(color_as_string c6);
 
val c7 = Gray 128;
print(color_as_string c7);
 
val c8 = Gray 255;
print(color_as_string c8);
 
val c9 = RGB(10, 20, 30);
print(color_as_string c9);

Poslední typ barvy: mix dvou barev (rekurzivní datový typ)

Nyní se dostáváme k nejzajímavější části námi vytvářeného typového systému barev. Přidáme do něj možnost definovat novou barvu mixem dvou jiných barev, přičemž bude možné specifikovat poměr obou barev. Například tedy budeme moci říci: „nová barva je z 10% tvořena základní červenou barvou a z 90% je tvořena RGB barvou (10, 20, 30)“ atd. Nebo samozřejmě můžeme novou barvu vytvořit mixem barev, které vznikly mixem jiných barev. Výsledkem je rekurzivní datový typ:

datatype color =
    BasicColor of basic_color * brightness
  | Gray of int
  | RGB of int * int * int
  | Mix of real * color * color
;
Poznámka: ze zápisu to možná není na první pohled patrné, ale skutečně lze barvu získat mixem většího množství jiných barev s uvedeném míchacího faktoru 0–1.

Samozřejmě budeme muset přidat novou větev do funkce to_rgb. V případě, že budeme chtít použít novou variantu Mix, je nutné z obou barev vypočítat jejich smíchané RGB složky. To jsem vyřešil vnořenou funkcí, protože zde dochází ke vzájemné rekurzi:

fun to_rgb (BasicColor c) : int * int * int =
    let val (color, brightness_level) = c
        val rgb = basic_color_to_rgb color
    in brightness rgb brightness_level
    end
  | to_rgb (Gray(g)) = (g,g,g)
  | to_rgb (RGB(r,g,b)) = (r,g,b)
  | to_rgb (Mix(ratio, c1, c2)) =
  let
    fun mix_colors ratio color1 color2 =
      let
        val (r1,g1,b1) = to_rgb color1
        val (r2,g2,b2) = to_rgb color2
      in
        (mix_component ratio r1 r2,
         mix_component ratio g1 g2,
         mix_component ratio b1 b2)
      end
  in mix_colors ratio c1 c2
  end
;

Samotný výpočet smíchané barvy není nijak komplikovaný, ovšem má jeden problém – voláme v něm opět funkci to_rgb, protože míchané barvy je nejprve nutné převést na jejich RGB složky:

    fun mix_colors ratio color1 color2 =
      let
        val (r1,g1,b1) = to_rgb color1
        val (r2,g2,b2) = to_rgb color2
      in
        (mix_component ratio r1 r2,
         mix_component ratio g1 g2,
         mix_component ratio b1 b2)
      end

Celý projekt s realizací poměrně komplikovaného datového typu color byl vytvořen pouze s využitím typového systému jazyka Standard ML (rekurzivní typy + n-tice + disjunktní zobrazení) a pattern matchingu. Jak je patrné, je celé řešení vlastně řešeno ortogonálně v porovnání s objektovým přístupem. V OO řešení by se (pravděpodobně) vytvořila hierarchie tříd, každá třída by měla vlastní realizaci to_rgb a stav i vlastní hodnota barvy by byla zapouzdřena a dostupná přes nějaké metody. Řešení na úrovni typů je odlišné – typ je jeden (i když rekurzivní) a i to_rgb je řešeno na jediném místě. Každý z použitých přístupů má pochopitelně své přednosti a zápory, které se týkají rozšiřitelnosti, čitelnosti, testovatelnosti apod.

Dále si povšimněte, že se v celém kódu setkáme pouze s jedním místem, v němž musíme konkrétně specifikovat datové typy (další dvě místa se týkají konverzních funkcí, ty jsou ale v jiných implementacích jazyka Standard ML součástí standardní knihovny). Jedná se o samotnou definici typu color. Ostatní zdrojový kód je již zcela založen na automaticky prováděné typové inferenci jazyka Standard ML:

datatype basic_color =
    Black
  | Red
  | Green
  | Yellow
  | Blue
  | Magenta
  | Cyan
  | White
;
 
 
datatype brightness =
    Dark
  | Bright
;
 
 
datatype color =
    BasicColor of basic_color * brightness
  | Gray of int
  | RGB of int * int * int
  | Mix of real * color * color
;
 
infix 0 |>;
fun x |> f = f x;
 
 
fun darker (r,g,b) =
  (r div 2, g div 2, b div 2)
;
 
 
fun brightness rgb Dark   = darker rgb
  | brightness rgb Bright = rgb
;
 
 
fun basic_color_to_rgb Black   = (0,   0,   0)
  | basic_color_to_rgb Red     = (255, 0,   0)
  | basic_color_to_rgb Green   = (0,   255, 0)
  | basic_color_to_rgb Yellow  = (255, 255, 0)
  | basic_color_to_rgb Blue    = (0,   0,   255)
  | basic_color_to_rgb Magenta = (255, 0,   255)
  | basic_color_to_rgb Cyan    = (0,   255, 255)
  | basic_color_to_rgb White   = (255, 255, 255)
  ;
 
 
fun i2s integer = Int.toString integer;
 
fun rgb_as_string (r, g, b) =
    "red=" ^ i2s(r) ^ ", green=" ^ i2s(g) ^ ", blue=" ^ i2s(b) ^ "\n"
;
 
fun real_of_int (i:int) : real =
    Real.fromInt i;
 
fun int_of_real (r:real) : int =
    Real.floor r;
 
fun mix_component ratio c1 c2 =
  int_of_real ((real_of_int c1) * ratio + (real_of_int c2) * (1.0 - ratio));
 
 
 
fun to_rgb (BasicColor c) : int * int * int =
    let val (color, brightness_level) = c
        val rgb = basic_color_to_rgb color
    in brightness rgb brightness_level
    end
  | to_rgb (Gray(g)) = (g,g,g)
  | to_rgb (RGB(r,g,b)) = (r,g,b)
  | to_rgb (Mix(ratio, c1, c2)) =
  let
    fun mix_colors ratio color1 color2 =
      let
        val (r1,g1,b1) = to_rgb color1
        val (r2,g2,b2) = to_rgb color2
      in
        (mix_component ratio r1 r2,
         mix_component ratio g1 g2,
         mix_component ratio b1 b2)
      end
  in mix_colors ratio c1 c2
  end
;
 
 
fun color_as_string c =
    c |> to_rgb |> rgb_as_string
;
 
 
val c1 = BasicColor(Black, Dark);
print(color_as_string c1);
 
val c2 = BasicColor(Black, Bright);
print(color_as_string c2);
 
val c3 = BasicColor(Yellow, Dark);
print(color_as_string c3);
 
val c4 = BasicColor(Yellow, Bright);
print(color_as_string c4);
 
val c5 = BasicColor(White, Dark);
print(color_as_string c5);
 
val c6 = BasicColor(White, Bright);
print(color_as_string c6);
 
val c7 = Gray 128;
print(color_as_string c7);
 
val c8 = Gray 255;
print(color_as_string c8);
 
val c9 = RGB(10, 20, 30);
print(color_as_string c9);
 
val c10 = Mix(0.5, BasicColor(Red, Bright), RGB(100, 100, 100));
print(color_as_string c10);
 
val c11 = Mix(0.5, BasicColor(Black, Dark), BasicColor(White, Bright));
print(color_as_string c11);

Otestování funkcionality:

red=0, green=0, blue=0
red=0, green=0, blue=0
red=127, green=127, blue=0
red=255, green=255, blue=0
red=127, green=127, blue=127
red=255, green=255, blue=255
red=128, green=128, blue=128
red=255, green=255, blue=255
red=10, green=20, blue=30
red=177, green=50, blue=50
red=127, green=127, blue=127

Transpřeklad poslední varianty colors6.sml do jazyka Lua a JavaScriptu

Na závěr, vzhledem k tomu, že je tento miniseriál věnován mj. i transpřekladači LunarML, si ukážeme, jakým způsobem se zdrojový kód z předchozí kapitoly přeložil do jazyků Lua i do JavaScriptu.

Začneme variantou přeloženou do formy kompatibilní s jazykem Lua. Výsledek vlastně ani není příliš dlouhý, i když Lua je (sémanticky) jazykem méně mocným, než Standard ML:

  Black = "Black"
  Red = "Red"
  Yellow = "Yellow"
  White = "White"
  Dark = "Dark"
  Bright = "Bright"
  rgb_as_string = function(r, g, b)
    local tmp = gsub(tostring(r), "-", "~")
    local tmp1 = "red=" .. tmp .. ", green="
    local tmp2 = gsub(tostring(g), "-", "~")
    local tmp3 = tmp1 .. tmp2 .. ", blue="
    local tmp4 = gsub(tostring(b), "-", "~")
    return tmp3 .. tmp4 .. "\n"
  end
  local mix_component = function(ratio, c1)
    return function(c2)
      local tmp = 1.0
      local tmp1 = c1 * tmp
      local tmp2 = tmp1 * ratio
      local tmp3 = 1.0
      local tmp4 = c2 * tmp3
      local tmp5 = tmp2 + tmp4 * (1.0 - ratio)
      local result = floor(tmp5)
      if math_type(result) == "integer" then
        return result
      elseif tmp5 ~= tmp5 then
        _raise(Domain, "real-1.sml:281:26")
      else
        _raise(_Overflow, "real-1.sml:283:26")
      end
    end
  end
  to_rgb = function(a)
    if a.tag == "BasicColor" then
      local c = a.payload
      local color = c[1]
      local brightness_level = c[2]
      local rgb
      if color == "Black" then
        rgb = {0, 0, 0}
      elseif color == "Red" then
        rgb = {255, 0, 0}
      elseif color == "Green" then
        rgb = {0, 255, 0}
      elseif color == "Yellow" then
        rgb = {255, 255, 0}
      elseif color == "Blue" then
        rgb = {0, 0, 255}
      elseif color == "Magenta" then
        rgb = {255, 0, 255}
      elseif color == "Cyan" then
        rgb = {0, 255, 255}
      elseif color == "White" then
        rgb = {255, 255, 255}
      else
        _raise(_Match, "colors_6.sml:40:5")
      end
      if brightness_level == "Dark" then
        local r = rgb[1]
        local g = rgb[2]
        local b = rgb[3]
        local tmp = r // 2
        local tmp1 = g // 2
        return {tmp, tmp1, b // 2}
      elseif brightness_level == "Bright" then
        return rgb
      else
        _raise(_Match, "colors_6.sml:35:5")
      end
    elseif a.tag == "Gray" then
      local g = a.payload
      return {g, g, g}
    elseif a.tag == "RGB" then
      local r = a.payload[1]
      local g = a.payload[2]
      return {r, g, a.payload[3]}
    elseif a.tag == "Mix" then
      local ratio, b1, b2, tmp, tmp1
      do
        ratio = a.payload[1]
        local c1 = a.payload[2]
        local c2 = a.payload[3]
        local exp = to_rgb(c1)
        local r1 = exp[1]
        local g1 = exp[2]
        b1 = exp[3]
        local exp1 = to_rgb(c2)
        local r2 = exp1[1]
        local g2 = exp1[2]
        b2 = exp1[3]
        local tmp2 = mix_component(ratio, r1)
        tmp = tmp2(r2)
        local tmp3 = mix_component(ratio, g1)
        tmp1 = tmp3(g2)
      end
      local tmp2 = mix_component(ratio, b1)
      local tmp3 = tmp2(b2)
      return {tmp, tmp1, tmp3}
    else
      _raise(_Match, "colors_6.sml:68:5")
    end
  end

Dtto, ale ve verzi pro JavaScript:

 const tmp12 = [NO_BUF];
 const stdOut = [{buffer: [null], buffer_mode: tmp12, writer: tmp11}];
 const Black = "Black";
 const Red = "Red";
 const Yellow = "Yellow";
 const White = "White";
 const Dark = "Dark";
 const Bright = "Bright";
 const rgb_as_string = function(r, g, b) {
  const tmp28 = toString(r);
  const tmp29 = _String_append(_String_append(Uint8Array.of(114, 101, 100, 61), tmp28), Uint8Array.of(44, 32, 103, 114, 101, 101, 110, 61));
  const tmp30 = toString(g);
  const tmp31 = _String_append(_String_append(tmp29, tmp30), Uint8Array.of(44, 32, 98, 108, 117, 101, 61));
  const tmp32 = toString(b);
  return _String_append(_String_append(tmp31, tmp32), Uint8Array.of(10));
 };
 const mix_component = function(ratio, c1) {
  return function(c2) {
   const tmp28 = c1 * ratio + c2 * (1.0 - ratio);
   const tmp29 = floor(tmp28);
   const tmp30 = isNaN(tmp29);
   if (tmp30) {
    throw Domain;
   } else if (tmp29 < - 2.147483648e9 || tmp29 > 2.147483647e9) {
    throw _Overflow;
   } else {
    const tmp31 = tmp29 | 0;
    return tmp31;
   }
  };
 };
 let to_rgb;
 to_rgb = function(a) {
  if (a.tag === "BasicColor") {
   const c = a.payload;
   const color = c[0];
   const brightness_level = c[1];
   let rgb;
   if (color === "Black") {
    rgb = [0, 0, 0];
   } else if (color === "Red") {
    rgb = [255, 0, 0];
   } else if (color === "Green") {
    rgb = [0, 255, 0];
   } else if (color === "Yellow") {
    rgb = [255, 255, 0];
   } else if (color === "Blue") {
    rgb = [0, 0, 255];
   } else if (color === "Magenta") {
    rgb = [255, 0, 255];
   } else if (color === "Cyan") {
    rgb = [0, 255, 255];
   } else if (color === "White") {
    rgb = [255, 255, 255];
   } else {
    throw _Match;
   }
   if (brightness_level === "Dark") {
    const r = rgb[0];
    const g = rgb[1];
    const b = rgb[2];
    const tmp28 = 0 + Math.floor(r / 2);
    const tmp29 = 0 + Math.floor(g / 2);
    return [tmp28, tmp29, 0 + Math.floor(b / 2)];
   } else if (brightness_level === "Bright") {
    return rgb;
   } else {
    throw _Match;
   }
  } else if (a.tag === "Gray") {
   const g = a.payload;
   return [g, g, g];
  } else if (a.tag === "RGB") {
   const r = a.payload[0];
   const g = a.payload[1];
   return [r, g, a.payload[2]];
  } else if (a.tag === "Mix") {
   const ratio = a.payload[0];
   const c1 = a.payload[1];
   const c2 = a.payload[2];
   const exp = to_rgb(c1);
   const r1 = exp[0];
   const g1 = exp[1];
   const b1 = exp[2];
   const exp1 = to_rgb(c2);
   const r2 = exp1[0];
   const g2 = exp1[1];
   const b2 = exp1[2];
   const tmp28 = mix_component(ratio, r1);
   const tmp29 = tmp28(r2);
   const tmp30 = mix_component(ratio, g1);
   const tmp31 = tmp30(g2);
   const tmp32 = mix_component(ratio, b1);
   const tmp33 = tmp32(b2);
   return [tmp29, tmp31, tmp33];
  } else {
   throw _Match;
  }
 };
Poznámka: na tomto místě je vhodné si připomenout, že i když ani Lua ani JavaScript nejsou jazyky se statickým typovým systémem, jsou oba zdrojové kódy naprosto typově bezpečné.

Příloha: Makefile pro transpřeklad všech demonstračních příkladů

Všechny dnes použité demonstrační příklady i příklady ukázané v článcích a byly přeloženy z jazyka Standard ML do jazyků Lua a JavaScript následujícím souborem Makefile:

Školení Zabbix

programs := \
        accumulate.lua \
        add_1.lua \
        add_2.lua \
        add_3.lua \
        add_4.lua \
        add_5.lua \
        anonymous_add_1.lua \
        anonymous_add_2.lua \
        anonymous_inc_1.lua \
        anonymous_inc_2.lua \
        colors_1.lua \
        colors_2.lua \
        colors_3.lua \
        colors_4.lua \
        colors_5.lua \
        colors_6.lua \
        exc_1.lua \
        exc_2.lua \
        exc_3.lua \
        exc_4.lua \
        exc_5.lua \
        exists.lua \
        fibonacci_func_call_1.lua \
        fibonacci_func_call_2.lua \
        fibonacci_naive.lua \
        fibonacci_pattern_matching.lua \
        hello.lua \
        length.lua \
        map.lua \
        mutual_recursion.lua \
        pipe.lua \
        pow_curry_1.lua \
        pow_curry_2.lua \
        pow_operator_1.lua \
        pow_operator_2.lua \
        pow_operator_3.lua \
        pow_real_operator_1.lua \
        pow_real_operator_2.lua \
        pow_real_operator_3.lua \
        some_none.lua \
        some_none_2.lua \
        string_join.lua \
        sum_int.lua \
        sum_real.lua \
        tuples.lua \
        accumulate.js \
        add_1.js \
        add_2.js \
        add_3.js \
        add_4.js \
        add_5.js \
        anonymous_add_1.js \
        anonymous_add_2.js \
        anonymous_inc_1.js \
        anonymous_inc_2.js \
        colors_1.js \
        colors_2.js \
        colors_3.js \
        colors_4.js \
        colors_5.js \
        colors_6.js \
        exc_1.js \
        exc_2.js \
        exc_3.js \
        exc_4.js \
        exc_5.js \
        exists.js \
        fibonacci_func_call_1.js \
        fibonacci_func_call_2.js \
        fibonacci_naive.js \
        fibonacci_pattern_matching.js \
        hello.js \
        length.js \
        map.js \
        mutual_recursion.js \
        pipe.js \
        pow_curry_1.js \
        pow_curry_2.js \
        pow_operator_1.js \
        pow_operator_2.js \
        pow_operator_3.js \
        pow_real_operator_1.js \
        pow_real_operator_2.js \
        pow_real_operator_3.js \
        some_none.js \
        some_none_2.js \
        string_join.js \
        sum_int.js \
        sum_real.js \
        tuples.js
 
 
all: $(programs)
 
clean:
        rm -f *.lua
 
.PHONY: all clean
 
%.lua: %.sml
        docker run -v work1:/work -w /work ghcr.io/minoki/lunarml:latest lunarml compile $<

%.js: %.sml
        docker run -v work1:/work -w /work ghcr.io/minoki/lunarml:latest lunarml compile --webjs $<

Repositář s demonstračními příklady

Všechny výše popsané demonstrační příklady (a to jak zdrojové kódy v SML, tak i výsledek transpřekladu do jazyků Lua a JavaScript) byly uloženy do repositáře dostupného na adrese https://github.com/tisnik/ml-examples/. V tabulce umístěné pod tímto odstavcem jsou uvedeny odkazy na tyto příklady:

# Příklad Popis příkladu Cesta
1 hello.sml klasický program typu „Hello world“ https://github.com/tisnik/ml-examples/tree/master/LunarML/hello.sml
2 hello.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/LunarML/hello.lua
3 hello.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/hello.js
       
4 add1.sml součet dvou proměnných se zobrazením výsledku součtu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/add1.sml
5 add1.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/add1.lua
6 add1.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/add1.js
       
7 add2.sml součet realizovaný uživatelskou funkcí https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/add2.sml
8 add2.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/add2.lua
9 add2.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/add2.js
       
10 add3.sml součet, který není možné snadno optimalizovat https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/add3.sml
11 add3.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/add3.lua
12 add3.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/add3.js
       
13 add4.sml součet, který není možné snadno optimalizovat, druhá varianta https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/add4.sml
14 add4.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/add4.lua
15 add4.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/add4.js
       
16 add5.sml součet, který není možné snadno optimalizovat, třetí varianta https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/add5.sml
17 add5.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/add5.lua
18 add5.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/add5.js
       
19 fibonacci_naive.sml výpočet n-tého členu Fibonacciho posloupnosti, rekurzivní varianta https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/fibonacci_naive.sml
20 fibonacci_naive.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/fibonacci_naive.lua
21 fibonacci_naive.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/fibonacci_naive.lua
       
22 fibonacci_pattern_matching.sml výpočet n-tého členu Fibonacciho posloupnosti, založeno na pattern matchingu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/fibonacci_pattern_matchin­g.sml
23 fibonacci_pattern_matching.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/fibonacci_pattern_matchin­g.lua
24 fibonacci_pattern_matching.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/fibonacci_pattern_matchin­g.lua
       
25 fibonacci_func_call1.sml složitější varianta výpočtu n-tého členu Fibonacciho posloupnosti https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/fibonacci_func_call1.sml
26 fibonacci_func_call1.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/fibonacci_func_call1.lua
27 fibonacci_func_call1.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/fibonacci_func_call1.lua
       
28 fibonacci_func_call2.sml složitější varianta výpočtu n-tého členu Fibonacciho posloupnosti https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/fibonacci_func_call2.sml
29 fibonacci_func_call2.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/fibonacci_func_call2.lua
30 fibonacci_func_call2.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/fibonacci_func_call2.lua
       
31 length.sml výpočet délky seznamu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/length.sml
32 length.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/length.lua
33 length.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/length.js
       
34 sum_int.sml součet všech prvků uložených v seznamu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/sum_int.sml
35 sum_int.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/sum_int.lua
36 sum_int.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/sum_int.js
       
37 sum_real.sml součet prvků s explicitním určením typu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/sum_real.sml
38 sum_real.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/sum_real.lua
39 sum_real.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/sum_real.js
       
40 string_join.sml realizace operace spojení řetězců předaných v seznamu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/string_join.sml
41 string_join.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/string_join.lua
42 string_join.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/string_join.js
       
43 some_none.sml typ option https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/some_none.sml
44 some_none.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/some_none.lua
45 some_none.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/some_none.js
       
46 some_none2.sml vynucení zavolání funkce divide a zpracování hodnoty SOME/NONE https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/some_none2.sml
47 some_none2.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/some_none2.lua
48 some_none2.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/some_none2.js
       
49 accumulate.sml funkce pro akumulaci mezivýsledků výpočtů https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/accumulate.sml
50 accumulate.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/accumulate.lua
51 accumulate.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/accumulate.js
       
52 exists.sml definice nového infixového operátoru https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/exists.sml
53 exists.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/exists.lua
54 exists.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/exists.js
       
55 mutual_recursion.sml dvě funkce realizující vzájemnou rekurzi https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/mutual_recursion.sml
56 mutual_recursion.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/mutual_recursion.lua
57 mutual_recursion.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/mutual_recursion.js 
       
58 pipe.sml operátor pro zřetězení několika operací https://github.com/tisnik/ml-examples/tree/master/LunarML/pipe.sml
59 pipe.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/LunarML/pipe.lua
60 pipe.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/pipe.js
       
61 tuples.sml základní operace s n-ticemi https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/tuples.sml
62 tuples.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/tuples.lua
63 tuples.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/tuples.js
       
64 anonymous_add1.sml definice a zavolání anonymní funkce se dvěma parametry https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/anonymous_add1.sml
65 anonymous_add1.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/anonymous_add1.lua
66 anonymous_add1.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/anonymous_add1.js
       
67 anonymous_add2.sml definice a zavolání anonymní funkce se dvěma parametry, komplikovanější vyhodnocení parametrů https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/anonymous_add2.sml
68 anonymous_add2.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/anonymous_add2.lua
69 anonymous_add2.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/anonymous_add2.js
       
70 anonymous_inc1.sml definice a zavolání anonymní funkce s jedním parametrem https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/anonymous_inc1.sml
71 anonymous_inc1.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/anonymous_inc1.lua
72 anonymous_inc1.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/anonymous_inc1.js
       
73 anonymous_inc2.sml definice a zavolání anonymní funkce s jedním parametrem, komplikovanější vyhodnocení parametrů https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/anonymous_inc2.sml
74 anonymous_inc2.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/anonymous_inc2.lua
75 anonymous_inc2.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/anonymous_inc2.js
       
76 map.sml funkce vyššího řádu map https://github.com/tisnik/ml-examples/tree/master/LunarML/map.sml
77 map.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/LunarML/map.lua
78 map.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/map.js
       
79 colors1.sml definice výčtového uživatelského datového typu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors1.sml
80 colors1.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors1.lua
81 colors1.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors1.js
       
82 colors2.sml převody mezi výčtovým uživatelským typem, n-ticí (trojicí) a řetězcem https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors2.sml
83 colors2.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors2.lua
84 colors2.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors2.js
       
85 colors3.sml komplikovanější datový typ reprezentující barvu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors3.sml
86 colors3.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors3.lua
87 colors3.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors3.js
       
88 colors4.sml reprezentace barvy několika různými způsoby https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors4.sml
89 colors4.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors4.lua
90 colors4.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors4.js
       
91 colors5.sml rekurzivní uživatelsky definovaný typ https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors5.sml
92 colors5.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors5.lua
93 colors5.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors5.js
       
94 colors6.sml rekurzivní uživatelsky definovaný typ https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors6.sml
95 colors6.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors6.lua
96 colors6.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/colors6.js
       
97 pow_curry1.sml currying v jazyku Standard ML, první příklad https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_curry1.sml
98 pow_curry1.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_curry1.lua
99 pow_curry1.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_curry1.js
       
100 pow_curry2.sml currying v jazyku Standard ML, druhý příklad https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_curry2.sml
101 pow_curry2.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_curry2.lua
102 pow_curry2.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_curry2.js
       
103 exc1.sml hodnota SOME/NONE pro reprezentaci potenciální chyby https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/exc1.sml
104 exc1.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/exc1.lua
105 exc1.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/exc1.js
       
106 exc2.sml vznik (vyhození) runtime výjimky https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/exc2.sml
107 exc2.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/exc2.lua
108 exc2.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/exc2.js
       
109 exc3.sml definice a vyhození vlastní výjimky https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/exc3.sml
110 exc3.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/exc3.lua
111 exc3.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/exc3.js
       
112 exc4.sml vlastní výjimka s parametrem https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/exc4.sml
113 exc4.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/exc4.lua
114 exc4.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/exc4.js
       
115 exc5.sml zachycení výjimky https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/exc5.sml
116 exc5.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/exc5.lua
117 exc5.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/LunarML/exc5.js
       
118 pow_operator1.sml definice a aplikace nového infixového operátoru https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_operator1.sml
119 pow_operator1.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_operator1.lua
120 pow_operator1.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_operator1.js
       
121 pow_operator2.sml definice a aplikace nového infixového operátoru s levou asociativitou https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_operator1.sml
122 pow_operator2.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_operator1.lua
123 pow_operator2.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_operator1.js
       
124 pow_operator3.sml definice a aplikace nového infixového operátoru s pravou asociativitou https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_operator1.sml
125 pow_operator3.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_operator1.lua
126 pow_operator3.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_operator1.js
       
127 pow_operator4.sml nekorektní varianta realizace nového operátoru https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_operator4.sml
       
128 pow_real_operator1.sml definice a aplikace nového infixového operátoru zapsaného znaky ** https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_real_operator1.sml
129 pow_real_operator1.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_real_operator1.lua
130 pow_real_operator1.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_real_operator1.js
       
131 pow_real_operator2.sml definice a aplikace nového infixového operátoru zapsaného znaky ** s levou asociativitou https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_real_operator1.sml
132 pow_real_operator2.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_real_operator1.lua
133 pow_real_operator2.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_real_operator1.js
       
134 pow_real_operator3.sml definice a aplikace nového infixového operátoru zapsaného znaky ** s pravou asociativitou https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_real_operator1.sml
135 pow_real_operator3.lua výsledek transpřekladu ze Standard ML do jazyka Lua https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_real_operator1.lua
136 pow_real_operator3.js výsledek transpřekladu ze Standard ML do JavaScriptu https://github.com/tisnik/ml-examples/tree/master/Lunar­ML/pow_real_operator1.js
       
137 Makefile pravidla (cíle) pro transpřeklad všech demonstračních příkladů https://github.com/tisnik/ml-examples/tree/master/LunarML/Makefile

Literatura

Poznámka: v této kapitole jsou uvedeny nejenom knihy o jazyku ML resp. Standard ML, ale i knihy o programovacím jazyku OCaml, který ze Standard ML ze značné míry vychází.
  1. ML for the Working Programmer
    https://www.cl.cam.ac.uk/~lp15/MLbo­ok/pub-details.html
  2. Elements of ML Programming, 2nd Edition (ML97)
    http://infolab.stanford.e­du/~ullman/emlp.html
  3. A tour of Standard ML
    https://saityi.github.io/sml-tour/tour/welcome
  4. The History of Standard ML
    https://smlfamily.github.i­o/history/SML-history.pdf
  5. The Standard ML Basis Library
    https://smlfamily.github.io/Basis/
  6. Programming in Standard ML
    http://www.cs.cmu.edu/~rwh/is­ml/book.pdf
  7. Programming in Standard ML '97: A Tutorial Introduction
    http://www.lfcs.inf.ed.ac­.uk/reports/97/ECS-LFCS-97–364/
  8. Programming in Standard ML '97: An On-line Tutorial
    https://homepages.inf.ed.ac­.uk/stg/NOTES/
  9. The OCaml system release 4.13
    https://ocaml.org/releases/4­.13/htmlman/index.html
  10. Real World OCaml: Functional programming for the masses
    https://dev.realworldocaml.org/
  11. OCaml from the Very Beginning
    http://ocaml-book.com/
  12. OCaml from the Very Beginning: More OCaml : Algorithms, Methods & Diversions
    http://ocaml-book.com/more-ocaml-algorithms-methods-diversions/
  13. Unix system programming in OCaml
    http://ocaml.github.io/ocamlunix/
  14. OCaml for Scientists
    https://www.ffconsultancy­.com/products/ocaml_for_sci­entists/index.html
  15. Using, Understanding, and Unraveling The OCaml Language
    https://caml.inria.fr/pub/docs/u3-ocaml/
  16. Developing Applications With objective Caml
    https://caml.inria.fr/pub/docs/oreilly-book/index.html
  17. Introduction to Objective Caml
    http://courses.cms.caltech­.edu/cs134/cs134b/book.pdf
  18. How to Think Like a (Functional) Programmer
    https://greenteapress.com/thin­kocaml/index.html
  19. Types and Programming Languages
    https://i.warosu.org/data/sci/im­g/0163/64/1725651701869705­.pdf
  20. Purely Functional Data Structures (Chris Okasaki)
    https://www.cs.cmu.edu/~rwh/stu­dents/okasaki.pdf

Předchozí články o jazyku ML/SML i o jazycích OCaml a F#

Jak již bylo napsáno v úvodu dnešního článku, na stránkách Roota jsme se již seznámili jak se základními koncepty jazyka ML/Standard ML, tak i s jazyky OCaml a F#, které z původního ML vychází:

  1. ML – funkcionální jazyk s revolučním typovým systémem
    https://www.root.cz/clanky/ml-funkcionalni-jazyk-s-revolucnim-typovym-systemem/
  2. Funkce a typový systém programovacího jazyka ML
    https://www.root.cz/clanky/funkce-a-typovy-system-programovaciho-jazyka-ml/
  3. Curryfikace (currying), výjimky a vlastní operátory v jazyku ML
    https://www.root.cz/clanky/curryfikace-currying-vyjimky-a-vlastni-operatory-v-jazyku-ml/
  4. Funkcionální programovací jazyk F#
    https://www.root.cz/clanky/fun­kcionalni-programovaci-jazyk-f/
  5. Programovací jazyk OCaml
    https://www.root.cz/clanky/pro­gramovaci-jazyk-ocaml/
  6. Programovací jazyk F#: proměnné, funkce a datové typy
    https://www.root.cz/clanky/pro­gramovaci-jazyk-f-promenne-funkce-a-datove-typy/
  7. Proměnné, funkce a datové typy v jazyku OCaml
    https://www.root.cz/clanky/promenne-funkce-a-datove-typy-v-jazyku-ocaml/
  8. Rekurze a pattern matching v programovacím jazyku F#
    https://www.root.cz/clanky/rekurze-a-pattern-matching-v-programovacim-jazyku-f/
  9. Práce se seznamy v jazyce F#
    https://www.root.cz/clanky/prace-se-seznamy-v-jazyce-f/
  10. Programovací jazyk OCaml: rekurze, pattern matching a práce se seznamy
    https://www.root.cz/clanky/pro­gramovaci-jazyk-ocaml-rekurze-pattern-matching-a-prace-se-seznamy/
  11. Datové typy Option, Result a Array v programovacím jazyku F#
    https://www.root.cz/clanky/datove-typy-option-result-a-array-v-programovacim-jazyku-f/
  12. Datové typy Option, Result a Array v programovacím jazyku OCaml
    https://www.root.cz/clanky/datove-typy-option-result-a-array-v-programovacim-jazyku-ocaml/
  13. Operátory v programovacím jazyku OCaml
    https://www.root.cz/clanky/operatory-v-programovacim-jazyku-ocaml/
  14. Operátory v programovacím jazyku F#
    https://www.root.cz/clanky/operatory-v-programovacim-jazyku-f/
  15. Definice uživatelských datových typů v jazyku F#
    https://www.root.cz/clanky/definice-uzivatelskych-datovych-typu-v-jazyku-f/
  16. Definice uživatelských datových typů v jazyku OCaml
    https://www.root.cz/clanky/definice-uzivatelskych-datovych-typu-v-jazyku-ocaml/
  17. Rekurzivní datové typy v jazyku OCaml
    https://www.root.cz/clanky/rekurzivni-datove-typy-v-jazyku-ocaml/
  18. Řídicí konstrukce v programovacím jazyku OCaml
    https://www.root.cz/clanky/ridici-konstrukce-v-programovacim-jazyku-ocaml/
  19. LunarML: až překvapivě kvalitní transpiler z jazyka Standard ML do jazyků Lua a JavaScript
    https://www.root.cz/clanky/lunarml-az-prekvapive-kvalitni-transpiler-z-jazyka-standard-ml-do-jazyku-lua-a-javascript/
  20. LunarML: definice a volání různých variant funkcí
    https://www.root.cz/clanky/lunarml-definice-a-volani-ruznych-variant-funkci/

Odkazy na Internetu

  1. LunarML documentation
    https://lunarml.readthedoc­s.io/en/latest/index.html
  2. Standard ML of New Jersey
    https://www.smlnj.org/
  3. Programming Languages: Standard ML – 1 (a navazující videa)
    https://www.youtube.com/wat­ch?v=2sqjUWGGzTo
  4. 6 Excellent Free Books to Learn Standard ML
    https://www.linuxlinks.com/excellent-free-books-learn-standard-ml/
  5. SOSML: The Online Interpreter for Standard ML
    https://sosml.org/
  6. ML (Computer program language)
    https://www.barnesandnoble­.com/b/books/other-programming-languages/ml-computer-program-language/_/N-29Z8q8Zvy7
  7. Strong Typing
    https://perl.plover.com/y­ak/typing/notes.html
  8. What to know before debating type systems
    http://blogs.perl.org/user­s/ovid/2010/08/what-to-know-before-debating-type-systems.html
  9. Types, and Why You Should Care (Youtube)
    https://www.youtube.com/wat­ch?v=0arFPIQatCU
  10. DynamicTyping (Martin Fowler)
    https://www.martinfowler.com/bli­ki/DynamicTyping.html
  11. DomainSpecificLanguage (Martin Fowler)
    https://www.martinfowler.com/bli­ki/DomainSpecificLanguage­.html
  12. Language Workbenches: The Killer-App for Domain Specific Languages?
    https://www.martinfowler.com/ar­ticles/languageWorkbench.html
  13. Effective ML (Youtube)
    https://www.youtube.com/watch?v=-J8YyfrSwTk
  14. Why OCaml (Youtube)
    https://www.youtube.com/wat­ch?v=v1CmGbOGb2I
  15. CSE 341: Functions and patterns
    https://courses.cs.washin­gton.edu/courses/cse341/04wi/lec­tures/03-ml-functions.html
  16. Comparing Objective Caml and Standard ML
    http://adam.chlipala.net/mlcomp/
  17. What are the key differences between Standard ML and OCaml?
    https://www.quora.com/What-are-the-key-differences-between-Standard-ML-and-OCaml?share=1
  18. Cheat Sheets (pro OCaml)
    https://www.ocaml.org/doc­s/cheat_sheets.html
  19. Syllabus (FAS CS51)
    https://cs51.io/college/syllabus/
  20. Abstraction and Design In Computation
    http://book.cs51.io/
  21. Learn X in Y minutes Where X=Standard ML
    https://learnxinyminutes.com/doc­s/standard-ml/
  22. CSE307 Online – Summer 2018: Principles of Programing Languages course
    https://www3.cs.stonybrook­.edu/~pfodor/courses/summer/cse307­.html
  23. CSE307 Principles of Programming Languages course: SML part 1
    https://www.youtube.com/wat­ch?v=p1n0_PsM6hw
  24. CSE 307 – Principles of Programming Languages – SML
    https://www3.cs.stonybrook­.edu/~pfodor/courses/summer/CSE307/L01_SML­.pdf
  25. SML, Some Basic Examples
    https://cs.fit.edu/~ryan/sml/in­tro.html
  26. History of programming languages
    https://devskiller.com/history-of-programming-languages/
  27. History of programming languages (Wikipedia)
    https://en.wikipedia.org/wi­ki/History_of_programming_lan­guages
  28. Jemný úvod do rozsáhlého světa jazyků LISP a Scheme
    https://www.root.cz/clanky/jemny-uvod-do-rozsahleho-sveta-jazyku-lisp-a-scheme/
  29. The Evolution Of Programming Languages
    https://www.i-programmer.info/news/98-languages/8809-the-evolution-of-programming-languages.html
  30. Evoluce programovacích jazyků
    https://ccrma.stanford.edu/cou­rses/250a-fall-2005/docs/ComputerLanguagesChart.png
  31. Poly/ML Homepage
    https://polyml.org/
  32. PolyConf 16: A brief history of F# / Rachel Reese
    https://www.youtube.com/wat­ch?v=cbDjpi727aY
  33. Programovací jazyk Clojure 18: základní techniky optimalizace aplikací
    https://www.root.cz/clanky/pro­gramovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  34. Moscow ML Language Overview
    https://itu.dk/people/ses­toft/mosml/mosmlref.pdf
  35. ForLoops
    http://mlton.org/ForLoops
  36. Funkcionální dobrodružství v JavaScriptu
    https://blog.kolman.cz/2015/12/fun­kcionalni-dobrodruzstvi-v-javascriptu.html
  37. Recenze knihy Functional Thinking (Paradigm over syntax)
    https://www.root.cz/clanky/recenze-knihy-functional-thinking-paradigm-over-syntax/
  38. Currying
    https://sw-samuraj.cz/2011/02/currying/
  39. Používání funkcí v F#
    https://docs.microsoft.com/cs-cz/dotnet/fsharp/tutorials/using-functions
  40. Funkce vyššího řádu
    http://naucte-se.haskell.cz/funkce-vyssiho-radu
  41. Currying (Wikipedia)
    https://en.wikipedia.org/wi­ki/Currying
  42. Currying (Haskell wiki)
    https://wiki.haskell.org/Currying
  43. Haskell Curry
    https://en.wikipedia.org/wi­ki/Haskell_Curry
  44. Moses Schönfinkel
    https://en.wikipedia.org/wi­ki/Moses_Sch%C3%B6nfinkel
  45. Fibonacci sequence
    https://en.wikipedia.org/wi­ki/Fibonacci_sequence
  46. Moonscript: jazyk inspirovaný CoffeeScriptem určený pro ekosystém jazyka Lua
    https://www.root.cz/clanky/moonscript-jazyk-inspirovany-coffeescriptem-urceny-pro-ekosystem-jazyka-lua/
  47. Moonscript: jazyk inspirovaný CoffeeScriptem určený pro ekosystém jazyka Lua (2)
    https://www.root.cz/clanky/moonscript-jazyk-inspirovany-coffeescriptem-urceny-pro-ekosystem-jazyka-lua-2/
  48. Moonscript: jazyk inspirovaný CoffeeScriptem určený pro ekosystém jazyka Lua (dokončení)
    https://www.root.cz/clanky/moonscript-jazyk-inspirovany-coffeescriptem-urceny-pro-ekosystem-jazyka-lua-dokonceni/
  49. Programovací jazyk Lua v roli skriptovacího jazyka pro WWW stránky
    https://www.root.cz/clanky/pro­gramovaci-jazyk-lua-v-roli-skriptovaciho-jazyka-pro-www-stranky/
  50. Transcrypt: technologie umožňující použití Pythonu v prohlížeči
    https://www.root.cz/clanky/transcrypt-technologie-umoznujici-pouziti-pythonu-v-prohlizeci/
  51. GopherJS: transpřekladač z jazyka Go do JavaScriptu
    https://www.root.cz/clanky/gopherjs-transprekladac-z-jazyka-go-do-javascriptu/
  52. Rychlost CPythonu 3.11 a 3.12 v porovnání s JIT a AOT překladači
    https://www.root.cz/clanky/rychlost-cpythonu-3–11-a-3–12-v-porovnani-s-jit-a-aot-prekladaci-pythonu/
  53. Seriál F# a OCaml
    https://www.root.cz/serialy/f-a-ocaml/
  54. Haskell or Standard ML for beginners?
    https://stackoverflow.com/qu­estions/810409/haskell-or-standard-ml-for-beginners#813646
  55. Awesome transpilers
    https://github.com/milahu/awesome-transpilers
  56. Languages that compile to JS (from CoffeeScript wiki)
    https://gist.github.com/mat­thiasak/c3c9c40d0f98ca91def1

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.