맨위로가기

표준 ML

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

표준 ML(SML)은 함수형 프로그래밍 언어이다. 표현식으로 구성되며, 함수가 핵심 요소이다. SML은 정적 타입 추론, 절(clause)에 따른 함수 정의, 람다 함수, 지역 정의, 타입 동의어, 대수적 자료형, 패턴 매칭, 고차 함수, 예외 처리, 모듈 시스템 등 다양한 기능을 제공한다. SML은 삽입 정렬, 병합 정렬, 퀵 정렬과 같은 알고리즘 구현에 사용될 수 있으며, 표현식 인터프리터 및 임의 정밀도 정수 연산을 지원한다. SML은 커링과 부분 적용을 통해 함수를 특수화할 수 있으며, 표준 ML 기본 라이브러리를 제공하고, 수치 계산, 그래픽스, 머신 러닝 등 다양한 분야의 서드 파티 라이브러리를 지원한다. MLton, Moscow ML, Poly/ML, SML/NJ, SML.NET, ML Kit 등 다양한 구현체가 있으며, Alice, SML#, SOSML, CakeML, Isabelle/ML, TILT와 같은 파생 언어 및 연구용 구현체도 존재한다. 코펜하겐 IT 대학교의 전사적 아키텍처, HOL, Isabelle, LEGO, Twelf와 같은 증명 보조자, 컴파일러, 집적 회로 설계 등 다양한 프로젝트에서 활용된다.

더 읽어볼만한 페이지

  • 1990년 개발된 프로그래밍 언어 - 하스켈
    하스켈은 해스켈 커리의 이름을 딴 순수 함수형 프로그래밍 언어로, 여러 함수형 언어 통합 노력의 결과로 탄생하여 느긋한 계산법, 패턴 매칭, 타입 클래스, 모나드 등의 특징을 가지며 GHC가 표준 구현체로 사용된다.
  • ML 프로그래밍 언어 계열 - OCaml
    OCaml은 ML 계열의 함수형 프로그래밍 언어로서 클래스 기반 객체 지향 프로그래밍 기능을 지원하며, 강력한 타입 시스템, 타입 추론, 꼬리 재귀 최적화 등의 특징을 가진다.
  • ML 프로그래밍 언어 계열 - ML (프로그래밍 언어)
    ML은 1970년대 초에 개발된 프로그래밍 언어로, 강력한 타입 시스템, 일급 함수, 가비지 컬렉션, 정적 타이핑 등의 기능을 제공하며 다양한 분야에서 활용된다.
  • 절차적 프로그래밍 언어 - C (프로그래밍 언어)
    C는 하드웨어 제어와 이식성이 뛰어난 고급 절차적 프로그래밍 언어로서, 다양한 분야에서 사용되며 후속 언어에 영향을 주었고, 성능과 효율성이 높지만 안전성 문제 개선이 필요한 언어이다.
  • 절차적 프로그래밍 언어 -
    펄은 래리 월이 개발한 텍스트 조작에 강점을 가진 다목적 프로그래밍 언어이며, 1987년 펄 1.0이 처음 공개된 이후 여러 버전 업데이트를 거쳐 객체 지향 프로그래밍과 유니코드 지원 기능을 추가했고, 현재 펄 5가 널리 사용되며 CPAN을 통해 방대한 모듈 생태계를 제공한다.
표준 ML - [IT 관련 정보]에 관한 문서
기본 정보
종류다중 패러다임 프로그래밍 언어
프로그래밍 패러다임함수형 프로그래밍, 명령형 프로그래밍, 모듈러 프로그래밍
ML 계열ML
발표 연도1983년
최신 버전 발표 연도1997년
타이핑추론, 정적, 스트롱
구현체SML/NJ, MLton, Poly/ML
방언Alice, 컨커런트 ML, 디펜던트 ML
영향을 받은 언어ML, Hope, Pascal
영향을 준 언어Elm, F#, F*, Haskell, OCaml, Python, Rust, Scala
파일 확장자.sml
웹사이트Standard ML 공식 웹사이트
설계
기타
참고 서적The Definition of Standard ML (Revised)

2. 언어

SML 프로그램은 표현식으로 구성되며, 핵심 기능은 함수이다.

== 함수 ==

함수는 추상화를 위해 사용되며, 계승(factorial) 함수는 다음과 같이 표현될 수 있다.

```sml

fun factorial n =

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

```

== 타입 추론 ==

SML 컴파일러는 사용자가 명시적으로 타입을 지정하지 않아도 정적 타입(static type)을 추론한다. 컴파일러는 `n`이 정수 표현식에만 사용되므로 자체적으로 정수여야 하며, 모든 터미널 표현식은 정수 표현식이어야 함을 추론할 수 있다.

== 선언적 정의 ==

함수는 절(clause)에 따라 정의될 수 있다. 동일한 함수는 절 함수 정의로 표현될 수 있으며, 여기서 ''if''-''then''-''else'' 조건은 특정 값에 대해 평가된 팩토리얼 함수의 템플릿으로 대체된다.

```sml

fun factorial 0 = 1

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

```

== 명령형 정의 ==

표준 ML에서는 반복문을 사용하여 함수를 정의할 수 있다.

```sml

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

```

== 람다 함수 ==

표준 ML에서는 익명 함수를 사용할 수 있다.

```sml

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

```

위 예시에서 `val` 키워드는 식별자를 값에 바인딩하고, `fn` 키워드는 익명 함수를 도입하며, `rec` 키워드는 정의가 자기 참조되도록 허용한다.

== 지역 정의 ==

표준 ML에서 불변성을 유지하는 꼬리 재귀적 타이트 루프를 불변성이 없는 외부 함수 내에서 하나 이상의 누산기 매개변수와 함께 캡슐화하는 것은 흔히 사용되는 방식이다.

지역 함수를 사용하면 더 효율적인 꼬리 재귀적 스타일로 다시 작성할 수 있다.

```sml

local

fun loop (0, acc) = acc

| loop (m, acc) = loop (m - 1, m * acc)

in

fun factorial n = loop (n, 1)

end

```

== 타입 동의어 ==

`type` 키워드를 사용하여 타입 동의어(type synonym)를 정의할 수 있다. 다음은 평면 상의 점에 대한 타입 동의어이며, 두 점 사이의 거리와 헤론의 공식에 따라 주어진 꼭짓점을 가진 삼각형의 면적을 계산하는 함수이다.

```text

type loc = real * real

fun square (x : real) = x * x

fun 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

```

== 대수적 자료형 ==

표준 ML은 대수적 자료형(ADT)에 대한 강력한 지원을 제공한다. 자료형은 튜플의 상호 배타적 합집합(또는 "곱의 합")으로 생각할 수 있다. 패턴 매칭 때문에 정의하고 사용하기 쉬우며, 대부분의 표준 ML 구현에서 패턴 완전성 검사와 패턴 중복 검사가 가능하다.

객체 지향 프로그래밍 언어에서 상호 배타적 합집합은 클래스 계층 구조로 표현할 수 있다. 그러나 클래스 계층 구조와 달리 ADT는 폐쇄 세계 가정을 따른다. 따라서 ADT의 확장성은 클래스 계층 구조의 확장성과 직교한다. 클래스 계층 구조는 동일한 인터페이스를 구현하는 새로운 하위 클래스로 확장할 수 있는 반면, ADT의 함수는 고정된 생성자 집합에 대해 확장할 수 있다. 표현 문제를 참조하라.

자료형은 `datatype` 키워드로 정의된다. 자료형 동의어는 재귀적으로 사용할 수 없음에 유의해야 한다. 재귀적 생성자를 정의하려면 자료형이 필요하다.

== 패턴 매칭 ==

패턴 매칭을 통해 함수 인수를 정의할 수 있다. 패턴은 정의된 순서대로 일치한다. C 프로그래머는 태그 값을 기반으로 하는 태그된 공용체를 사용하여 데이터 유형과 패턴 일치를 수행하는 것과 동일한 작업을 할 수 있지만, ML의 정적 검사는 컴파일 시간에 프로그램의 정확성에 대한 강력한 보장을 제공한다.

함수 인수는 다음과 같이 패턴으로 정의할 수 있다.

```sml

fun area (Circle (_, r)) = Math.pi * square r

| area (Square (_, s)) = square s

| area (Triangle p) = heron p (* 위에 참조 *)

```

인수가 패턴으로 정의된 함수의 "절 형식"은 case 표현식에 대한 구문 설탕이다.

```sml

fun area shape = case shape of

Circle (_, r) => Math.pi * square r

| Square (_, s) => square s

| Triangle p => heron p

```

패턴 완전성 검사(exhaustiveness checking)는 데이터 타입의 각 생성자가 적어도 하나의 패턴과 일치하는지 확인한다. 패턴 중복 검사(redundancy checking)또한 가능하다.

2의 정수 거듭제곱 길이를 가진 수열의 1차원 이산 웨이블릿 변환은 리스트 상의 패턴 매치의 좋은 예시가 된다. 패턴 매칭은 복잡한 변환을 명확하고 간결하게 표현할 수 있으며, SML 컴파일러는 패턴 매칭에 최적화된 코드를 생성하므로, 코드가 간결해질 뿐만 아니라 고속으로 실행된다.

== 고차 함수 ==

표준 ML에서 함수는 다른 함수를 인수로 받거나 결과값으로 반환할 수 있다. 예를 들어, `List.map` 함수는 표준 ML에서 자주 사용되는 고차 함수 중 하나이다.

```sml

fun map _ [] = []

| map f (x :: xs) = f x :: map f xs

```

위 함수는 꼬리 재귀 `List.foldl`을 사용하여 더 효율적으로 구현할 수 있다.

```sml

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

```

함수를 인수로 받아 사용하는 예시로 함수의 수치 미분이 있다.

```sml


  • fun d delta f x =

(f (x + delta) - f (x - delta)) / (2.0 * delta);

val d = fn : real -> (real -> real) -> real -> real

```

이 함수 `d`는 작은 값 `delta`를 필요로 하며, 타입은 `real`을 다른 함수(타입 `(real -> real) -> real -> real`)에 적용하는 형태이다. 이러한 방식을 커링이라고 부른다. `delta` 값을 고정하여 특화된 함수를 만들 수 있다.

```sml

  • val d = d 1E~8;

val d = fn : (real -> real) -> real -> real

```

`d`의 타입을 보면 `real -> real` 타입의 함수를 첫 번째 인수로 받는 것을 알 수 있다. 예를 들어 x3-x-1에서 x=3일 때의 미분값 근사는 다음과 같이 계산한다.

```sml

  • d (fn x => x * x * x - x - 1.0) 3.0;

val it = 25.9999996644 : real

```

올바른 값은 26이다.

`d`처럼 다른 함수를 인수로 받는 함수를 고차 함수라고 부른다. 커링과 고차 함수는 어댑터 패턴처럼 불필요한 코드 제거에 활용될 수 있다.

== 예외 ==

`raise` 키워드를 사용하여 예외(exception)를 발생시키고, `handle` 구문을 사용하여 처리한다. 예외 시스템은 비지역 탈출을 구현할 수 있다.

```sml

local

exception Zero;

val p = fn (0, _) => raise Zero | (a, b) => a * b

in

fun prod xs = List.foldl p 1 xs handle Zero => 0

end

```

위 코드에서 `exception Zero`가 발생하면 제어 흐름이 함수 `List.foldl`를 완전히 벗어난다. 예외가 발생하면 제어 흐름이 프레임 전체 체인을 건너뛰어 관련 계산을 피할 수 있다. 와일드카드 패턴으로 밑줄(_)이 사용되었다.

== 모듈 시스템 ==

표준 ML의 고급 모듈 시스템은 프로그램을 논리적으로 관련된 형과 값의 선언인 ''structure''를 통해 계층으로 분해할 수 있다. SML 모듈은 단순히 네임스페이스를 제어하는 것뿐만 아니라 추상화 역할도 수행하며, 프로그래머는 이를 사용하여 추상 데이터형을 정의할 수 있다.

SML 모듈 시스템은 주로 세 가지 구문 요소로 구성된다. ''signature'', ''structure'', ''functor''가 그것이다.

=== 시그니처 ===

인터페이스는 구조체의 타입으로 간주되며, 구조체에서 제공하는 모든 엔티티의 이름, 각 타입 구성 요소의 아리티, 각 값 구성 요소의 타입, 각 하위 구조체의 시그니처를 지정한다. 타입 구성 요소의 정의는 선택 사항이며, 정의가 숨겨진 타입 구성 요소는 ''추상 타입''이다.

예를 들어, 의 시그니처는 다음과 같을 수 있다.

```sml

signature QUEUE = sig

type 'a queue

exception QueueError

val empty : 'a queue

val isEmpty : 'a queue -> bool

val singleton : 'a -> 'a queue

val fromList : 'a list -> 'a queue

val insert : 'a * 'a queue -> 'a queue

val peek : 'a queue -> 'a

val remove : 'a queue -> 'a * 'a queue

end

```

이 시그니처는 `queue`의 다형성 타입, `exception QueueError` 및 큐에 대한 기본 연산을 정의하는 값을 제공하는 모듈을 설명한다.

=== 구조체 ===

''구조체(structure)''는 모듈 그 자체이다. 형, 식, 값, 그리고 ''structure'' ('substructure')의 집합체이며, 이들을 하나의 논리 단위로 묶는다.

큐 구조체는 다음과 같이 구현할 수 있다.

```sml

structure TwoListQueue :> QUEUE = struct

type 'a queue = 'a list * 'a list

exception QueueError

val empty = ([], [])

fun isEmpty ([], []) = true

| isEmpty _ = false

fun 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 outs

fun remove (_, []) = raise QueueError

| remove (ins, [a]) = (a, ([], List.rev ins))

| remove (ins, a :: outs) = (a, (ins, outs))

end

```

이 정의는 `TwoListQueue`가 `QUEUE` 시그니처를 구현한다고 선언한다. 또한 `:> `로 표시되는 ''불투명한 귀속''은 시그니처에 정의되지 않은 유형(예: `type 'a queue`)은 추상적이어야 함을 나타낸다. 즉, 큐를 리스트 쌍으로 정의하는 것은 모듈 외부에서 볼 수 없다. 구조체는 시그니처의 모든 정의를 구현한다.

''structure''를 사용하려면, 점 표기법을 사용하여 해당 형이나 값과 같은 멤버에 접근하면 된다. 예를 들어, 문자열 큐의 형은 `string TwoListQueue.queue`, 빈 큐는 `TwoListQueue.empty`, `q`라는 큐의 첫 번째 요소를 삭제하려면 `TwoListQueue.remove q`라고 작성하면 된다.

=== 펀터 ===

펀터(functor)는 구조체에서 구조체로의 함수이다. 즉, 펀터는 하나 이상의 인수를 받아들이는데, 이는 일반적으로 주어진 시그니처의 구조이며, 결과를 구조로 생성한다. 펀터는 제네릭 자료 구조와 알고리즘을 구현하는 데 사용된다.[8]

트리의 너비 우선 탐색을 위한 한 가지 인기 있는 알고리즘[8]은 큐를 사용한다. 다음은 추상 큐 구조에 대해 매개변수화된 해당 알고리즘의 버전이다.

```sml

(* Okasaki, ICFP, 2000 이후 *)

functor BFS (Q: QUEUE) = struct

datatype 'a tree = E | T of 'a * 'a tree * 'a tree

local

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

end

structure QueueBFS = BFS (TwoListQueue)

```

위 코드에서 `BFS` 펀터 내에서 큐의 표현은 보이지 않는다. 큐의 구현에 무관하게 너비 우선 탐색을 수행할 수 있도록 데이터 추상화 메커니즘이 적용되었다.

2. 1. 함수

함수는 추상화를 위해 사용되며, 계승(factorial) 함수는 다음과 같이 표현될 수 있다.

```sml

fun factorial n =

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

2. 2. 타입 추론

SML 컴파일러는 사용자가 명시적으로 타입을 지정하지 않아도 정적 타입(static type)을 추론한다. 컴파일러는 `n`이 정수 표현식에만 사용되므로 자체적으로 정수여야 하며, 모든 터미널 표현식은 정수 표현식이어야 함을 추론할 수 있다.

2. 3. 선언적 정의

함수는 절(clause)에 따라 정의될 수 있다. 동일한 함수는 절 함수 정의로 표현될 수 있으며, 여기서 ''if''-''then''-''else'' 조건은 특정 값에 대해 평가된 팩토리얼 함수의 템플릿으로 대체된다.

```sml

fun factorial 0 = 1

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

2. 4. 명령형 정의

표준 ML에서는 반복문을 사용하여 함수를 정의할 수 있다.

```sml

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

2. 5. 람다 함수

표준 ML에서는 익명 함수를 사용할 수 있다.

```sml

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

```

위 예시에서 `val` 키워드는 식별자를 값에 바인딩하고, `fn` 키워드는 익명 함수를 도입하며, `rec` 키워드는 정의가 자기 참조되도록 허용한다.

2. 6. 지역 정의

표준 ML에서 불변성을 유지하는 꼬리 재귀적 타이트 루프를 불변성이 없는 외부 함수 내에서 하나 이상의 누산기 매개변수와 함께 캡슐화하는 것은 흔히 사용되는 방식이다.

지역 함수를 사용하면 더 효율적인 꼬리 재귀적 스타일로 다시 작성할 수 있다.

```sml

local

fun loop (0, acc) = acc

| loop (m, acc) = loop (m - 1, m * acc)

in

fun factorial n = loop (n, 1)

end

2. 7. 타입 동의어

`type` 키워드를 사용하여 타입 동의어(type synonym)를 정의할 수 있다. 다음은 평면 상의 점에 대한 타입 동의어이며, 두 점 사이의 거리와 헤론의 공식에 따라 주어진 꼭짓점을 가진 삼각형의 면적을 계산하는 함수이다.

```text

type loc = real * real

fun square (x : real) = x * x

fun 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

2. 8. 대수적 자료형

표준 ML은 대수적 자료형(ADT)에 대한 강력한 지원을 제공한다. 자료형은 튜플의 상호 배타적 합집합(또는 "곱의 합")으로 생각할 수 있다. 패턴 매칭 때문에 정의하고 사용하기 쉬우며, 대부분의 표준 ML 구현에서 패턴 완전성 검사와 패턴 중복 검사가 가능하다.

객체 지향 프로그래밍 언어에서 상호 배타적 합집합은 클래스 계층 구조로 표현할 수 있다. 그러나 클래스 계층 구조와 달리 ADT는 폐쇄 세계 가정을 따른다. 따라서 ADT의 확장성은 클래스 계층 구조의 확장성과 직교한다. 클래스 계층 구조는 동일한 인터페이스를 구현하는 새로운 하위 클래스로 확장할 수 있는 반면, ADT의 함수는 고정된 생성자 집합에 대해 확장할 수 있다. 표현 문제를 참조하라.

자료형은 `datatype` 키워드로 정의된다. 자료형 동의어는 재귀적으로 사용할 수 없음에 유의해야 한다. 재귀적 생성자를 정의하려면 자료형이 필요하다.

2. 9. 패턴 매칭

패턴 매칭(pattern matching)을 통해 함수 인수를 정의할 수 있다. 패턴은 정의된 순서대로 일치한다. C 프로그래머는 태그 값을 기반으로 하는 태그된 공용체를 사용하여 데이터 유형과 패턴 일치를 수행하는 것과 동일한 작업을 할 수 있지만, ML의 정적 검사는 컴파일 시간에 프로그램의 정확성에 대한 강력한 보장을 제공한다.

함수 인수는 다음과 같이 패턴으로 정의할 수 있다.

```sml

fun area (Circle (_, r)) = Math.pi * square r

| area (Square (_, s)) = square s

| area (Triangle p) = heron p (* 위에 참조 *)

```

인수가 패턴으로 정의된 함수의 "절 형식"은 case 표현식에 대한 구문 설탕이다.

```sml

fun area shape = case shape of

Circle (_, r) => Math.pi * square r

| Square (_, s) => square s

| Triangle p => heron p

```

패턴 완전성 검사(exhaustiveness checking)는 데이터 타입의 각 생성자가 적어도 하나의 패턴과 일치하는지 확인한다. 패턴 중복 검사(redundancy checking)또한 가능하다.

2의 정수 거듭제곱 길이를 가진 수열의 1차원 이산 웨이블릿 변환은 리스트 상의 패턴 매치의 좋은 예시가 된다. 패턴 매칭은 복잡한 변환을 명확하고 간결하게 표현할 수 있으며, SML 컴파일러는 패턴 매칭에 최적화된 코드를 생성하므로, 코드가 간결해질 뿐만 아니라 고속으로 실행된다.

2. 10. 고차 함수

표준 ML에서 함수는 다른 함수를 인수로 받거나 결과값으로 반환할 수 있다. 예를 들어, `List.map` 함수는 표준 ML에서 자주 사용되는 고차 함수 중 하나이다.

```sml

fun map _ [] = []

| map f (x :: xs) = f x :: map f xs

```

위 함수는 꼬리 재귀 `List.foldl`을 사용하여 더 효율적으로 구현할 수 있다.

```sml

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

```

함수를 인수로 받아 사용하는 예시로 함수의 수치 미분이 있다.

```sml

  • fun d delta f x =

(f (x + delta) - f (x - delta)) / (2.0 * delta);

val d = fn : real -> (real -> real) -> real -> real

```

이 함수 `d`는 작은 값 `delta`를 필요로 하며, 타입은 `real`을 다른 함수(타입 `(real -> real) -> real -> real`)에 적용하는 형태이다. 이러한 방식을 커링이라고 부른다. `delta` 값을 고정하여 특화된 함수를 만들 수 있다.

```sml

  • val d = d 1E~8;

val d = fn : (real -> real) -> real -> real

```

`d`의 타입을 보면 `real -> real` 타입의 함수를 첫 번째 인수로 받는 것을 알 수 있다. 예를 들어 x3-x-1에서 x=3일 때의 미분값 근사는 다음과 같이 계산한다.

```sml

  • d (fn x => x * x * x - x - 1.0) 3.0;

val it = 25.9999996644 : real

```

올바른 값은 26이다.

`d`처럼 다른 함수를 인수로 받는 함수를 고차 함수라고 부른다. 커링과 고차 함수는 어댑터 패턴처럼 불필요한 코드 제거에 활용될 수 있다.

2. 11. 예외

`raise` 키워드를 사용하여 예외(exception)를 발생시키고, `handle` 구문을 사용하여 처리한다. 예외 시스템은 비지역 탈출을 구현할 수 있다.

```sml

local

exception Zero;

val p = fn (0, _) => raise Zero | (a, b) => a * b

in

fun prod xs = List.foldl p 1 xs handle Zero => 0

end

```

위 코드에서 `exception Zero`가 발생하면 제어 흐름이 함수 `List.foldl`를 완전히 벗어난다. 예외가 발생하면 제어 흐름이 프레임 전체 체인을 건너뛰어 관련 계산을 피할 수 있다. 와일드카드 패턴으로 밑줄(_)이 사용되었다.

2. 12. 모듈 시스템

표준 ML의 고급 모듈 시스템은 프로그램을 논리적으로 관련된 형과 값의 선언인 ''structure''를 통해 계층으로 분해할 수 있다. SML 모듈은 단순히 네임스페이스를 제어하는 것뿐만 아니라 추상화 역할도 수행하며, 프로그래머는 이를 사용하여 추상 데이터형을 정의할 수 있다.

SML 모듈 시스템은 주로 세 가지 구문 요소로 구성된다. ''signature'', ''structure'', ''functor''가 그것이다.

; signature

: 인터페이스는 구조체의 타입으로 간주되며, 구조체에서 제공하는 모든 엔티티의 이름, 각 타입 구성 요소의 아리티, 각 값 구성 요소의 타입, 각 하위 구조체의 시그니처를 지정한다. 타입 구성 요소의 정의는 선택 사항이며, 정의가 숨겨진 타입 구성 요소는 ''추상 타입''이다.

: 예를 들어, 의 시그니처는 다음과 같을 수 있다.

```sml

signature QUEUE = sig

type 'a queue

exception QueueError

val empty : 'a queue

val isEmpty : 'a queue -> bool

val singleton : 'a -> 'a queue

val fromList : 'a list -> 'a queue

val insert : 'a * 'a queue -> 'a queue

val peek : 'a queue -> 'a

val remove : 'a queue -> 'a * 'a queue

end

```

: 이 시그니처는 `queue`의 다형성 타입, `exception QueueError` 및 큐에 대한 기본 연산을 정의하는 값을 제공하는 모듈을 설명한다.

; structure

: ''구조체(structure)''는 모듈 그 자체이다. 형, 식, 값, 그리고 ''structure'' ('substructure')의 집합체이며, 이들을 하나의 논리 단위로 묶는다.

: 큐 구조체는 다음과 같이 구현할 수 있다.

```sml

structure TwoListQueue :> QUEUE = struct

type 'a queue = 'a list * 'a list

exception QueueError

val empty = ([], [])

fun isEmpty ([], []) = true

| isEmpty _ = false

fun 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 outs

fun remove (_, []) = raise QueueError

| remove (ins, [a]) = (a, ([], List.rev ins))

| remove (ins, a :: outs) = (a, (ins, outs))

end

```

: 이 정의는 `TwoListQueue`가 `QUEUE` 시그니처를 구현한다고 선언한다. 또한 `:> `로 표시되는 ''불투명한 귀속''은 시그니처에 정의되지 않은 유형(예: `type 'a queue`)은 추상적이어야 함을 나타낸다. 즉, 큐를 리스트 쌍으로 정의하는 것은 모듈 외부에서 볼 수 없다. 구조체는 시그니처의 모든 정의를 구현한다.

: ''structure''를 사용하려면, 점 표기법을 사용하여 해당 형이나 값과 같은 멤버에 접근하면 된다. 예를 들어, 문자열 큐의 형은 `string TwoListQueue.queue`, 빈 큐는 `TwoListQueue.empty`, `q`라는 큐의 첫 번째 요소를 삭제하려면 `TwoListQueue.remove q`라고 작성하면 된다.

; functor

: 펀터(functor)는 구조체에서 구조체로의 함수이다. 즉, 펀터는 하나 이상의 인수를 받아들이는데, 이는 일반적으로 주어진 시그니처의 구조이며, 결과를 구조로 생성한다. 펀터는 제네릭 자료 구조와 알고리즘을 구현하는 데 사용된다.[8]

: 트리의 너비 우선 탐색을 위한 한 가지 인기 있는 알고리즘[8]은 큐를 사용한다. 다음은 추상 큐 구조에 대해 매개변수화된 해당 알고리즘의 버전이다.

```sml

(* Okasaki, ICFP, 2000 이후 *)

functor BFS (Q: QUEUE) = struct

datatype 'a tree = E | T of 'a * 'a tree * 'a tree

local

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

end

structure QueueBFS = BFS (TwoListQueue)

```

: 위 코드에서 `BFS` 펀터 내에서 큐의 표현은 보이지 않는다. 큐의 구현에 무관하게 너비 우선 탐색을 수행할 수 있도록 데이터 추상화 메커니즘이 적용되었다.

2. 12. 1. 시그니처

인터페이스는 구조체의 타입으로 간주되며, 구조체에서 제공하는 모든 엔티티의 이름, 각 타입 구성 요소의 아리티, 각 값 구성 요소의 타입, 각 하위 구조체의 시그니처를 지정한다. 타입 구성 요소의 정의는 선택 사항이며, 정의가 숨겨진 타입 구성 요소는 ''추상 타입''이다.

예를 들어, 큐의 시그니처는 다음과 같을 수 있다.

```sml

signature QUEUE = sig

type 'a queue

exception QueueError;

val empty : 'a queue

val isEmpty : 'a queue -> bool

val singleton : 'a -> 'a queue

val fromList : 'a list -> 'a queue

val insert : 'a * 'a queue -> 'a queue

val peek : 'a queue -> 'a

val remove : 'a queue -> 'a * 'a queue

end

```

이 시그니처는 `queue`의 다형성 타입, `exception QueueError` 및 큐에 대한 기본 연산을 정의하는 값을 제공하는 모듈을 설명한다.

2. 12. 2. 구조체

wikitext

표준 ML에는 고도화된 모듈 시스템이 있어, 프로그램을 논리적으로 관련된 형과 값의 선언인 ''structure''를 통해 계층으로 분해할 수 있다. SML 모듈은 단순히 네임스페이스를 제어하는 것뿐만 아니라 추상화 역할도 수행하며, 프로그래머는 이를 사용하여 추상 데이터형을 정의할 수 있다.

''구조체(structure)''는 모듈 그 자체이다. 형, 식, 값, 그리고 ''structure'' ('substructure')의 집합체이며, 이들을 하나의 논리 단위로 묶는다.

큐 구조체는 다음과 같이 구현할 수 있다.

```sml

structure TwoListQueue :> QUEUE = struct

type 'a queue = 'a list * 'a list

exception QueueError;

val empty = ([], [])

fun isEmpty ([], []) = true

| isEmpty _ = false

fun 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 outs

fun remove (_, []) = raise QueueError

| remove (ins, [a]) = (a, ([], List.rev ins))

| remove (ins, a :: outs) = (a, (ins, outs))

end

```

이 정의는 가 를 구현한다고 선언한다. 또한 로 표시되는 ''불투명한 귀속''은 시그니처에 정의되지 않은 유형(예: )은 추상적이어야 함을 나타낸다. 즉, 큐를 리스트 쌍으로 정의하는 것은 모듈 외부에서 볼 수 없다. 구조체는 시그니처의 모든 정의를 구현한다.

''structure''를 사용하려면, 점 표기법을 사용하여 해당 형이나 값과 같은 멤버에 접근하면 된다. 예를 들어, 문자열 큐의 형은 `string TwoListQueue.queue`, 빈 큐는 `TwoListQueue.empty`, `q`라는 큐의 첫 번째 요소를 삭제하려면 `TwoListQueue.remove q`라고 작성하면 된다.

2. 12. 3. 펀터

펀터(functor)는 구조체에서 구조체로의 함수이다. 즉, 펀터는 하나 이상의 인수를 받아들이는데, 이는 일반적으로 주어진 시그니처의 구조이며, 결과를 구조로 생성한다. 펀터는 제네릭 자료 구조와 알고리즘을 구현하는 데 사용된다.[8]

트리의 너비 우선 탐색을 위한 한 가지 인기 있는 알고리즘[8]은 큐를 사용한다. 다음은 추상 큐 구조에 대해 매개변수화된 해당 알고리즘의 버전이다.

```sml

(* Okasaki, ICFP, 2000 이후 *)

functor BFS (Q: QUEUE) = struct

datatype 'a tree = E | T of 'a * 'a tree * 'a tree

local

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

end

structure QueueBFS = BFS (TwoListQueue)

```

내에서 큐의 표현은 보이지 않는다. 보다 구체적으로, 사용되는 표현이 실제로 두 개의 리스트 큐인 경우, 첫 번째 리스트를 선택할 방법이 없다. 이러한 데이터 추상화 메커니즘은 너비 우선 탐색을 큐의 구현에 진정으로 무관하게 만든다. 이는 일반적으로 바람직하다. 이 경우, 큐 구조는 추상화라는 방어벽 뒤에서 정확성에 의존하는 모든 논리적 불변성을 안전하게 유지할 수 있다.

```sml

(* Okasaki, ICFP, 2000 이후 *)

functor BFS (Q: QUEUE) = struct

datatype 'a tree = E | T of 'a * 'a tree * 'a tree

local

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

end

structure QueueBFS = BFS (TwoListQueue)

```

위 코드에서 `BFS` 펀터 내에서 큐의 표현은 보이지 않는다. 큐의 구현에 무관하게 너비 우선 탐색을 수행할 수 있도록 데이터 추상화 메커니즘이 적용되었다.

3. 코드 예제

SML 코드 조각은 대화형 최상위 수준에 입력하여 가장 쉽게 학습할 수 있다.

SML의 많은 처리 시스템은 대화형 최상위 레벨을 갖추고 있어, 여기에 입력하여 코드 조각을 쉽게 실행할 수 있다. 이는 입력된 식의 타입을 추론하고 평가한다. 예를 들어, SML/NJ에서는 다음과 같이 시작한다.

```text

$ sml

Standard ML of New Jersey v110.52 [built: Fri Jan 21 16:42:10 2005]




```

코드는 - 프롬프트 위치에 입력한다. 예를 들어 1+2*3을 계산하는 경우, 다음과 같다.

```text

  • 1 + 2 * 3;

val it = 7 : int

```

최상위 레벨은 식의 타입이 int임을 추론하고, 그 값 7을 표시한다.

== Hello, world! ==

다음은 "Hello, World!" 프로그램 예시이다.

hello.sml
bash



프로그램 hello.sml은 다음과 같다.

print "Hello world!\n";

MLton으로 컴파일하려면 다음과 같이 입력한다.

$ mlton hello.sml

실행 결과는 다음과 같다.

$ ./hello

Hello world!

$

== 알고리즘 ==

표준 ML을 사용하면 삽입 정렬, 병합 정렬, 퀵 정렬 등의 알고리즘을 구현할 수 있다.

다음은 오름차순으로 정렬되는 `int list`에 대한 삽입 정렬을 간결하게 표현한 것이다.

```sml

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 []

```

병합 정렬은 `split`, `merge`, `mergesort`의 세 함수로 구현된다. `split` 함수는 입력을 무시하고 `true`와 `false`를 번갈아 사용하는 상태(state) 클로저로 구현된다. `merge` 함수는 정렬된 두 목록을 하나의 정렬된 목록으로 병합한다.

```sml

(* 목록을 거의 절반으로 분할하며, 길이가 같거나,


  • 첫 번째 목록이 다른 목록보다 요소가 하나 더 많습니다.
  • O(n) 시간에 실행되며, 여기서 n = |xs|.
  • )

fun split xs = List.partition (alternator {}) xs

```

```sml

(* 정렬 순서 cmp를 사용하여 정렬된 두 목록을 병합합니다.

  • 사전 조건: 각 목록은 이미 cmp에 따라 정렬되어야 합니다.
  • O(n) 시간에 실행되며, 여기서 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

```

```sml

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

(* 주어진 정렬 연산 cmp에 따라 목록을 정렬합니다.

  • O(n log n) 시간에 실행되며, 여기서 n = |xs|.
  • )

fun mergesort cmp [] = []

| mergesort cmp [x] = [x]

| mergesort cmp xs = (merge cmp o ap (mergesort cmp) o split) xs

```

퀵 정렬은 다음과 같이 표현할 수 있다.

```sml

infix <<

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

```

힌들리-밀너 타입 추론을 사용하면 함수 `cmp`의 타입과 같이 복잡한 타입까지도 모든 변수의 타입을 추론할 수 있다.

== 표현식 인터프리터 ==

표준 ML을 사용하면 작은 표현식 언어를 정의하고 처리하는 인터프리터를 상대적으로 쉽게 구현할 수 있다.

```sml

exception TyErr;

datatype ty = IntTy | BoolTy

fun unify (IntTy, IntTy) = IntTy

| unify (BoolTy, BoolTy) = BoolTy

| unify (_, _) = raise TyErr

datatype exp

= True

| False

| Int of int

| Not of exp

| Add of exp * exp

| If of exp * exp * exp

fun 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 = (infer e; SOME (eval e)) handle TyErr => NONE

```

잘 형식화된 표현식과 잘못 형식화된 표현식에 대한 사용 예시는 다음과 같다.

```sml

val SOME (Int 3) = run (Add (Int 1, Int 2)) (* 잘 형식화됨 *)

val NONE = run (If (Not (Int 1), True, False)) (* 잘못 형식화됨 *)

```

== 임의 정밀도 정수 ==

표준 ML에서 `IntInf` 모듈은 임의 정밀도 정수 산술을 제공한다. 코드에 나타나는 정수는 자릿수에 상관없이 프로그래머가 신경 쓸 필요가 없다.

다음은 임의 정밀도 팩토리얼 함수를 구현한 예시이다.

fact.sml
bash



== 부분 적용 ==

표준 ML에서 함수는 커링(currying)과 부분 적용(partial application)을 통해 특수화될 수 있다. 예를 들어, `d` 함수는 주어진 함수 `f`의 숫자 미분을 점 `x`에서 계산하는데, `delta` 값을 부분 적용하여 특수화할 수 있다.

```sml


  • fun d delta f x = (f (x + delta) - f (x - delta)) / (2.0 * delta)

val d = fn : real -> (real -> real) -> real -> real

  • val d' = d 1E~8;

val d' = fn : (real -> real) -> real -> real

```

`d` 함수의 타입은 `real`을 `(real -> real) -> real -> real` 타입의 함수로 매핑하며, 이를 통해 인수를 부분적으로 적용할 수 있다. `d'`는 `delta` 값이 `1E~8`로 고정된 특수화된 함수이다.

`d'` 함수를 사용하여 f(x) = x^3-x-1x=3에서의 미분 근사값을 계산하면 25.9999996644를 얻을 수 있다. 이는 실제 미분값 26에 근접한 값이다.

이처럼 커링과 부분 적용은 코드를 간결하게 하고 재사용성을 높이는 데 유용하다. 특히, 고차 함수와 함께 사용될 때 어댑터 패턴과 같이 중복 코드를 제거하고 공통 부분을 팩터 아웃하는 데 활용될 수 있다.

3. 1. Hello, world!

다음은 "Hello, World!" 프로그램 예시이다.

hello.sml
bash



프로그램 hello.sml은 다음과 같다.

print "Hello world!\n";

MLton으로 컴파일하려면 다음과 같이 입력한다.

$ mlton hello.sml

실행 결과는 다음과 같다.

$ ./hello

Hello world!

$

3. 2. 알고리즘

표준 ML을 사용하면 삽입 정렬, 병합 정렬, 퀵 정렬 등의 알고리즘을 구현할 수 있다.

다음은 오름차순으로 정렬되는 `int list`에 대한 삽입 정렬을 간결하게 표현한 것이다.

```sml

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 []

```

병합 정렬은 `split`, `merge`, `mergesort`의 세 함수로 구현된다. `split` 함수는 입력을 무시하고 `true`와 `false`를 번갈아 사용하는 상태(state) 클로저로 구현된다. `merge` 함수는 정렬된 두 목록을 하나의 정렬된 목록으로 병합한다.

```sml

(* 목록을 거의 절반으로 분할하며, 길이가 같거나,

  • 첫 번째 목록이 다른 목록보다 요소가 하나 더 많습니다.
  • O(n) 시간에 실행되며, 여기서 n = |xs|.
  • )

fun split xs = List.partition (alternator {}) xs

```

```sml

(* 정렬 순서 cmp를 사용하여 정렬된 두 목록을 병합합니다.

  • 사전 조건: 각 목록은 이미 cmp에 따라 정렬되어야 합니다.
  • O(n) 시간에 실행되며, 여기서 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

```

```sml

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

(* 주어진 정렬 연산 cmp에 따라 목록을 정렬합니다.

  • O(n log n) 시간에 실행되며, 여기서 n = |xs|.
  • )

fun mergesort cmp [] = []

| mergesort cmp [x] = [x]

| mergesort cmp xs = (merge cmp o ap (mergesort cmp) o split) xs

```

퀵 정렬은 다음과 같이 표현할 수 있다.

```sml

infix <<

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

```

힌들리-밀너 타입 추론을 사용하면 함수 `cmp`의 타입과 같이 복잡한 타입까지도 모든 변수의 타입을 추론할 수 있다.

3. 3. 표현식 인터프리터

표준 ML을 사용하면 작은 표현식 언어를 정의하고 처리하는 인터프리터를 상대적으로 쉽게 구현할 수 있다.

```sml

exception TyErr;

datatype ty = IntTy | BoolTy

fun unify (IntTy, IntTy) = IntTy

| unify (BoolTy, BoolTy) = BoolTy

| unify (_, _) = raise TyErr

datatype exp

= True

| False

| Int of int

| Not of exp

| Add of exp * exp

| If of exp * exp * exp

fun 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 = (infer e; SOME (eval e)) handle TyErr => NONE

```

잘 형식화된 표현식과 잘못 형식화된 표현식에 대한 사용 예시는 다음과 같다.

```sml

val SOME (Int 3) = run (Add (Int 1, Int 2)) (* 잘 형식화됨 *)

val NONE = run (If (Not (Int 1), True, False)) (* 잘못 형식화됨 *)

3. 4. 임의 정밀도 정수

표준 ML에서 `IntInf` 모듈은 임의 정밀도 정수 산술을 제공한다. 코드에 나타나는 정수는 자릿수에 상관없이 프로그래머가 신경 쓸 필요가 없다.

다음은 임의 정밀도 팩토리얼 함수를 구현한 예시이다.

fact.sml
bash


3. 5. 부분 적용

표준 ML에서 함수는 커링(currying)과 부분 적용(partial application)을 통해 특수화될 수 있다. 예를 들어, `d` 함수는 주어진 함수 `f`의 숫자 미분을 점 `x`에서 계산하는데, `delta` 값을 부분 적용하여 특수화할 수 있다.

```sml

  • fun d delta f x = (f (x + delta) - f (x - delta)) / (2.0 * delta)

val d = fn : real -> (real -> real) -> real -> real

  • val d' = d 1E~8;

val d' = fn : (real -> real) -> real -> real

```

`d` 함수의 타입은 `real`을 `(real -> real) -> real -> real` 타입의 함수로 매핑하며, 이를 통해 인수를 부분적으로 적용할 수 있다. `d'`는 `delta` 값이 `1E~8`로 고정된 특수화된 함수이다.

`d'` 함수를 사용하여 f(x) = x^3-x-1x=3에서의 미분 근사값을 계산하면 25.9999996644를 얻을 수 있다. 이는 실제 미분값 26에 근접한 값이다.

이처럼 커링과 부분 적용은 코드를 간결하게 하고 재사용성을 높이는 데 유용하다. 특히, 고차 함수와 함께 사용될 때 어댑터 패턴과 같이 중복 코드를 제거하고 공통 부분을 팩터 아웃하는 데 활용될 수 있다.

4. 라이브러리

4. 1. 표준

표준 ML 기본 라이브러리[2]는 표준화되어 있으며 대부분의 구현에 함께 제공된다. 트리, 배열 및 기타 데이터 구조, 입출력 및 시스템 인터페이스를 위한 모듈을 제공한다.

4. 2. 서드 파티

표준 ML에서는 수치 계산을 위한 매트릭스 모듈과 카이로 그래픽스 라이브러리를 위한 오픈 소스 인터페이스인 cairo-sml, 그래픽 모델을 위한 머신 러닝 라이브러리등을 지원한다.

5. 구현체

MLton, Moscow ML, Poly/ML, 뉴저지 표준 ML(SML/NJ), SML.NET, ML Kit 등 다양한 표준 ML 구현체가 존재한다.


  • MLton은 전체 프로그램 최적화 컴파일러로, 정의를 엄격히 준수하며 다른 ML 구현에 비해 매우 빠른 코드를 생성한다.
  • Moscow ML은 Caml Light 런타임 엔진을 기반으로 하는 경량 구현으로, 모듈 및 대부분의 기본 라이브러리를 포함한 전체 표준 ML 언어를 구현한다.
  • Poly/ML은 빠른 코드를 생성하고 멀티코어 하드웨어(Portable Operating System Interface(POSIX) 스레드를 통해)를 지원하는 표준 ML의 전체 구현이다. 런타임 시스템은 병렬 가비지 수집 및 불변의 하부 구조의 온라인 공유를 수행한다.
  • 뉴저지 표준 ML은 동시 ML을 지원하는 관련 라이브러리, 도구, 대화형 셸 및 설명서가 포함된 전체 컴파일러이다.
  • SML.NET은 다른 .NET 프레임워크 코드와 연결하기 위한 확장이 있는 Common Language Runtime용 표준 ML 컴파일러이다.
  • ML Kit은 정의에 매우 가깝게 기반하여 영역 기반 메모리 관리를 영역의 자동 추론과 통합하여 실시간 응용 프로그램을 지원하는 것을 목표로 하는 구현이다.


Alice는 futures, 지연 평가, 분산 컴퓨팅을 통한 원격 프로시저 호출제약 프로그래밍을 사용하여 병렬 프로그래밍을 지원하는 자를란트 대학교의 표준 ML 인터프리터이다.

SML#은 레코드 다형성 및 C 언어 상호 운용성을 제공하는 SML의 확장이다. 이는 기존의 네이티브 컴파일러이며 그 이름은 .NET 프레임워크에서 실행되는 것에 대한 암시가 ''아닙니다''.

SOSML(https://github.com/SOSML/SOSML)은 TypeScript로 작성된 구현으로, SML 언어의 대부분과 기본 라이브러리의 일부를 지원한다.

CakeML은 런타임과 어셈블러로의 번역이 공식적으로 검증된 ML의 REPL 버전이다.

Isabelle은 공식 표준 ML(SML'97), Isabelle/ML 방언 및 증명 언어에 대한 정교한 IDE( jEdit 기반)를 사용하여 병렬 Poly/ML을 대화형 정리 증명기에 통합한다. Isabelle2016부터 ML용 소스 수준 디버거도 있다.

Poplog는 Common Lisp 및 Prolog와 함께 표준 ML 버전을 구현하여 혼합 언어 프로그래밍을 허용합니다. 모두 POP-11로 구현되어 증분적으로 컴파일된다.

TILT는 형식화된 중간 언어를 사용하여 코드를 최적화하고 정확성을 보장하며 형식화된 어셈블리 언어로 컴파일할 수 있는 표준 ML의 전체 인증 컴파일러이다.

이러한 모든 구현은 오픈 소스이며 자유롭게 사용할 수 있다. 대부분은 표준 ML 자체로 구현됩니다. 더 이상 상용 구현은 없다. 현재는 존재하지 않는 Harlequin은 한때 MLWorks라는 상용 IDE와 컴파일러를 제작했는데, 이는 Xanalys로 이전되었고 나중에 2013년 4월 26일 Ravenbrook Limited에 인수된 후 오픈 소스로 전환되었다.

5. 1. 표준

표준 ML은 MLton, Moscow ML, Poly/ML, 뉴저지 표준 ML(SML/NJ), SML.NET, ML Kit 등 다양한 구현체가 존재한다.

  • MLton은 전체 프로그램 최적화 컴파일러로, 정의를 엄격히 준수하며 다른 ML 구현에 비해 매우 빠른 코드를 생성한다.
  • Moscow ML은 Caml Light 런타임 엔진을 기반으로 하는 경량 구현으로, 모듈 및 대부분의 기본 라이브러리를 포함한 전체 표준 ML 언어를 구현한다.
  • Poly/ML은 빠른 코드를 생성하고 멀티코어 하드웨어를 지원하는 표준 ML의 전체 구현이다. 런타임 시스템은 병렬 가비지 수집 및 불변의 하부 구조의 온라인 공유를 수행한다.
  • 뉴저지 표준 ML은 동시 ML을 지원하는 관련 라이브러리, 도구, 대화형 셸 및 설명서가 포함된 전체 컴파일러이다.
  • SML.NET은 다른 .NET 프레임워크 코드와 연결하기 위한 확장이 있는 Common Language Runtime용 표준 ML 컴파일러이다.
  • ML Kit은 정의에 매우 가깝게 기반하여 가비지 수집기와 영역 기반 메모리 관리를 영역의 자동 추론과 통합하여 실시간 응용 프로그램을 지원하는 것을 목표로 하는 구현이다.


이 외에도, HaMLet, Alice, SML#, SOSML, CakeML, Isabelle, Poplog, TILT 등 다양한 표준 ML의 구현체 및 파생형들이 연구 및 개발되었다. 이들 구현체는 대부분 오픈 소스이며, 자유롭게 사용할 수 있다. 상용 구현은 더 이상 존재하지 않지만, 과거 Harlequin에서 MLWorks라는 상용 IDE와 컴파일러를 제작한 적이 있으며, 이는 이후 Ravenbrook Limited에 인수되어 오픈 소스로 전환되었다.

5. 2. 파생

표준 ML에는 여러 파생 언어 및 구현체가 존재한다.

  • Alice는 자를란트 대학교에서 개발한 표준 ML 인터프리터로, futures, 지연 평가, 분산 컴퓨팅을 통한 원격 프로시저 호출제약 프로그래밍을 사용해 병렬 프로그래밍을 지원한다.
  • SML#은 도호쿠 대학 전기통신연구소의 오호리 준 연구실에서 개발되었으며, 레코드 다형성 및 C 언어 상호 운용성을 제공하는 SML의 확장이다. 이는 기존의 네이티브 컴파일러이며 .NET 프레임워크와는 관련이 없다.
  • SOSML(https://github.com/SOSML/SOSML)은 TypeScript로 작성된 구현으로, SML 언어의 대부분과 기본 라이브러리의 일부를 지원한다.


이 외에도, 연구 목적으로 개발된 다양한 버전들이 존재한다.

5. 3. 연구

CakeML, Isabelle/ML, TILT 등 연구 목적으로 개발된 표준 ML 구현체도 존재한다. CakeML은 런타임과 어셈블러로의 번역이 공식적으로 검증된 ML의 REPL 버전이다. Isabelle/ML은 공식 표준 ML(SML'97), Isabelle/ML 방언 및 증명 언어에 대한 정교한 IDE(jEdit 기반)를 사용하여 병렬 Poly/ML을 대화형 정리 증명기에 통합한다. Isabelle2016부터 ML용 소스 수준 디버거도 있다. TILT는 형식화된 중간 언어를 사용하여 코드를 최적화하고 정확성을 보장하며 형식화된 어셈블리 언어로 컴파일할 수 있는 표준 ML의 전체 인증 컴파일러이다. Poplog은 Common Lisp 및 Prolog와 함께 표준 ML 버전을 구현하여 혼합 언어 프로그래밍을 허용하며, 모두 POP-11로 구현되어 증분적으로 컴파일된다.

6. 주요 프로젝트

코펜하겐 IT 대학교의 전체 전사적 아키텍처는 직원 기록, 급여, 과정 관리 및 피드백, 학생 프로젝트 관리, 웹 기반 셀프 서비스 인터페이스 등 약 10만 줄의 SML로 구현되어 있다.[4]

증명 보조자 HOL4, Isabelle, LEGO, 그리고 Twelf는 Standard ML로 작성되었다.[9] 또한 컴파일러 작성자 및 집적 회로 설계자, 예를 들어 ARM에서도 사용된다.[9]

참조

[1] 웹사이트 Influences - The Rust Reference https://doc.rust-lan[...] 2023-12-31
[2] 웹사이트 Standard ML Basis Library https://smlfamily.gi[...] 2022-01-10
[3] 웹사이트 Programming in Standard ML: Hierarchies and Parameterization https://www.cs.cmu.e[...] 2020-02-22
[4] 논문 Standard ML language
[5] 웹사이트 SML '97 http://www.smlnj.org[...]
[6] 서적 The Definition of Standard ML (Revised) MIT Press
[7] 웹사이트 itertools — Functions creating iterators for efficient looping — Python 3.7.1rc1 documentation https://docs.python.[...]
[8] 학회자료 Breadth-First Numbering: Lessons from a Small Exercise in Algorithm Design ACM
[9] 학회자료 The Semantics of Power and ARM Multiprocessor Machine Code http://www0.cs.ucl.a[...] 2009
[10] 서적 The Definition of Standard ML (Revised) https://smlfamily.gi[...] MIT Press 1997
[11] 웹인용 Programming in Standard ML: Hierarchies and Parameterization https://www.cs.cmu.e[...] 2020-02-22
[12] 웹인용 SML '97 http://www.smlnj.org[...]
[13] 웹인용 itertools — Functions creating iterators for efficient looping — Python 3.7.1rc1 documentation https://docs.python.[...]
[14] 서적 The Definition of Standard ML (Revised)



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com