Một trong số ít những điều mà tôi không thích về cuốn sách của Okasaki về các cấu trúc dữ liệu chức năng thuần túy là mã của anh ta bị lấp đầy bởi sự phù hợp với mô hình không đầy đủ. Ví dụ, tôi sẽ cho anh ta thực hiện các hàng đợi thời gian thực (được tái cấu trúc để loại bỏ các đình chỉ không cần thiết):
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
Như có thể thấy rotate
là không đầy đủ, bởi vì nó không bao gồm trường hợp danh sách phía sau trống. Hầu hết các triển khai ML tiêu chuẩn sẽ tạo ra một cảnh báo về nó. Chúng tôi biết rằng danh sách phía sau có thể trống, bởi vì rotate
điều kiện tiên quyết là danh sách phía sau dài hơn một phần tử so với luồng phía trước. Nhưng trình kiểm tra loại không biết - và không thể biết được, vì thực tế này không thể diễn tả được trong hệ thống loại ML.
Ngay bây giờ, giải pháp của tôi để ngăn chặn cảnh báo này là hack không liên tục sau đây:
fun rotate (x ::: $xs, y :: ys, zs) = x ::: $rotate (xs, ys, y ::: $zs)
| rotate (_, ys, zs) = foldl (fn (x, xs) => x ::: $xs) zs ys
Nhưng những gì tôi thực sự muốn là một hệ thống loại có thể hiểu rằng không phải mọi bộ ba là một đối số hợp lệ rotate
. Tôi muốn hệ thống loại cho phép tôi xác định các loại như:
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)
Và sau đó suy ra:
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
Tuy nhiên, tôi không muốn các loại phụ thuộc toàn diện, hoặc thậm chí GADT, hoặc bất kỳ thứ điên rồ nào khác mà các lập trình viên nhất định sử dụng. Tôi chỉ muốn xác định các kiểu con bằng cách khắc ra các tập hợp con được xác định theo quy tắc của các kiểu ML hiện có. Điều này có khả thi không?