หนึ่งในสองสามสิ่งที่ฉันไม่ชอบเกี่ยวกับหนังสือของโอกาซากิในโครงสร้างข้อมูลที่ใช้งานได้อย่างหมดจดคือโค้ดของเขาเกลื่อนไปด้วยการจับคู่รูปแบบที่ไม่สิ้นสุด ตัวอย่างเช่นฉันจะให้การใช้คิวแบบเรียลไทม์ของเขา (ปรับปรุงใหม่เพื่อกำจัดการระงับที่ไม่จำเป็น):
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 ประเภทที่มีอยู่ เป็นไปได้หรือไม่