विशुद्ध रूप से कार्यात्मक डेटा संरचनाओं पर ओकासाकी की पुस्तक के बारे में कुछ चीजें जो मुझे नापसंद हैं, वह यह है कि उनका कोड अटूट पैटर्न से मेल खाता है। एक उदाहरण के रूप में, मैं वास्तविक समय की कतारों के उनके कार्यान्वयन (अनावश्यक निलंबन को खत्म करने के लिए रिफलेक्ट किया गया) को दूंगा:
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
कि यह संपूर्ण नहीं है, क्योंकि यह उस मामले को कवर नहीं करता है जहां पीछे की सूची खाली है। अधिकांश मानक एमएल कार्यान्वयन इसके बारे में एक चेतावनी उत्पन्न करेंगे। हम जानते हैं कि पीछे की सूची संभवतः खाली नहीं हो सकती है, क्योंकि rotate
पूर्व शर्त यह है कि पीछे की सूची एक तत्व सामने की धारा से अधिक लंबी है। लेकिन टाइप चेकर को पता नहीं है - और यह संभवतः नहीं जान सकता है, क्योंकि यह तथ्य एमएल के टाइप सिस्टम में अपरिहार्य है।
अभी, इस चेतावनी को दबाने का मेरा उपाय निम्नलिखित अप्रभावी हैक है:
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
हालाँकि, मैं पूर्ण-निर्भर आश्रित प्रकार, या यहाँ तक कि GADTs, या किसी भी अन्य क्रेज़ी चीज़ों का उपयोग नहीं करना चाहता जो कुछ प्रोग्रामर उपयोग करते हैं। मैं केवल "नक्काशी बाहर" द्वारा उप-प्रकारों को परिभाषित करना चाहता हूं जो मौजूदा एमएल प्रकारों के व्यक्तिगत रूप से परिभाषित उप-समूह हैं। क्या यह संभव है?