我对Okasaki关于纯功能数据结构的书不满意的几件事之一是,他的代码充满了不尽详尽的模式匹配。举例来说,我将为他提供实时队列的实现(重构为消除不必要的挂起):
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类型的归纳定义子集来定义子类型。这可行吗?