순전히 기능적인 데이터 구조에 대한 오카 사키의 책에 대해 내가 싫어하는 몇 가지 중 하나는 코드가 완전한 패턴 일치로 흩어져 있다는 것입니다. 예를 들어, 실시간 대기열 구현 (불필요한 중단을 제거하기 위해 리팩토링)을 제공합니다.
infixr 5 :::
datatype 'a stream = Nil | ::: of 'a * 'a stream lazy
structure RealTimeQueue :> QUEUE =
struct
(* front stream, rear list, schedule stream *)
type 'a queue = 'a stream * 'a list * 'a stream
(* the front stream is one element shorter than the rear list *)
fun rotate (x ::: $xs, y :: ys, zs) = x ::: $rotate (xs, ys, y ::: $zs)
| rotate (Nil, y :: nil, zs) = y ::: $zs
fun exec (xs, ys, _ ::: $zs) = (xs, ys, zs)
| exec args = let val xs = rotate args in (xs, nil, xs) end
(* public operations *)
val empty = (Nil, nil, Nil)
fun snoc ((xs, ys, zs), y) = exec (xs, y :: ys, zs)
fun uncons (x ::: $xs, ys, zs) = SOME (x, exec (xs, ys, zs))
| uncons _ = NONE
end
볼 수 있듯이 rotate후면 목록이 비어있는 경우를 다루지 않기 때문에 철저하지 않습니다. 대부분의 표준 ML 구현은 이에 대한 경고를 생성합니다. 우리 는 리어리스트가 비어있을 수 없다는 것을 알고 있습니다. 왜냐하면 rotate리어리스트는 프론트 스트림보다 하나의 요소를 길게하는 것입니다. 그러나 타입 체커 는 알지 못하며 알 수 없습니다.이 사실은 ML의 타입 시스템에서 표현할 수 없기 때문입니다.
현재이 경고를 억제하는 솔루션은 다음과 같은 우아한 해킹입니다.
fun rotate (x ::: $xs, y :: ys, zs) = x ::: $rotate (xs, ys, y ::: $zs)
| rotate (_, ys, zs) = foldl (fn (x, xs) => x ::: $xs) zs ys
그러나 내가 정말로 원하는 것은 모든 트리플렛이 유효한 인수가 아니라는 것을 이해할 수있는 타입 시스템입니다 rotate. 형식 시스템에서 다음과 같은 형식을 정의 할 수 있기를 바랍니다.
type 'a triplet = 'a stream * 'a list * 'a stream
subtype 'a queue of 'a triplet
= (Nil, nil, Nil)
| (xs, ys, zs) : 'a queue => (_ ::: $xs, _ :: ys, zs)
| (xs, ys, zs) : 'a queue => (_ ::: $xs, ys, _ ::: $zs)
그리고 유추하십시오 :
subtype 'a rotatable of 'a triplet
= (xs, ys, _) : 'a rotatable => (_ ::: $xs, _ :: ys, _)
| (Nil, y :: nil, _)
subtype 'a executable of 'a triplet
= (xs, ys, zs) : 'a queue => (xs, ys, _ ::: $zs)
| (xs, ys, Nil) : 'a rotatable => (xs, ys, Nil)
val rotate : 'a rotatable -> 'a stream
val exec : 'a executable -> 'a queue
그러나 나는 전적으로 의존하는 유형, 심지어 GADT 또는 특정 프로그래머가 사용하는 다른 미친 것들을 원하지 않습니다. 기존 ML 유형의 유도 적으로 정의 된 부분 집합을 "조각"하여 하위 유형을 정의하려고합니다. 이것이 가능합니까?