Scala Language
निरंतरता पुस्तकालय
खोज…
परिचय
कंटीन्यूअस पासिंग स्टाइल नियंत्रण प्रवाह का एक रूप है जिसमें "निरंतरता" तर्क के रूप में बाकी गणना कार्यों को पास करना शामिल है। विचाराधीन फ़ंक्शन बाद में प्रोग्राम निष्पादन जारी रखने के लिए जारी रखता है। एक निरंतरता के बारे में सोचने का एक तरीका बंद करना है। स्काला निरंतरता पुस्तकालय भाषा के लिए आदिम shift
/ reset
के रूप में सीमांकित निरंतरता लाता है।
निरंतरता पुस्तकालय: https://github.com/scala/scala-continuations
वाक्य - विन्यास
- रीसेट {...} // निरंतरता एन्कोडिंग रीसेट ब्लॉक के अंत तक विस्तारित होती है
- शिफ्ट {...} // कॉल के बाद से एक निरंतरता बनाएं, इसे बंद करने के लिए
- A @cpsParam [B, C] // एक अभिकलन जिसके लिए C का मान बनाने के लिए एक फ़ंक्शन A => B की आवश्यकता होती है
- @cps [A] // @cpsParam [A, A] के लिए उपनाम
- @ एसपीएसपरम [यूनिट, यूनिट] के लिए
टिप्पणियों
shift
और reset
आदिम नियंत्रण प्रवाह संरचनाएं हैं, जैसे Int.+
एक आदिम ऑपरेशन है और Long
एक आदिम प्रकार है। वे अधिक से अधिक आदिम हैं कि सीमांकित निरंतरता का उपयोग वास्तव में लगभग सभी नियंत्रण प्रवाह संरचनाओं के निर्माण के लिए किया जा सकता है। वे बहुत उपयोगी नहीं हैं "आउट-ऑफ-द-बॉक्स", लेकिन वे वास्तव में चमकते हैं जब उन्हें समृद्ध एपीआई बनाने के लिए पुस्तकालयों में उपयोग किया जाता है।
निरंतरता और मठ भी निकटता से जुड़े हुए हैं। निरंतरता में बनाया जा सकता है निरंतरता इकाई , और क्योंकि उनके monads निरंतरता हैं flatMap
आपरेशन पैरामीटर के रूप में एक निरंतरता लेता है।
कॉलबैक निरंतरता हैं
// Takes a callback and executes it with the read value
def readFile(path: String)(callback: Try[String] => Unit): Unit = ???
readFile(path) { _.flatMap { file1 =>
readFile(path2) { _.foreach { file2 =>
processFiles(file1, file2)
}}
}}
readFile
लिए फ़ंक्शन तर्क एक निरंतरता है, उस readFile
में प्रोग्राम निष्पादन को जारी रखने के लिए यह आह्वान करता है कि readFile
अपना काम किया है।
आसानी से कॉलबैक नरक बन सकता है, इस पर लगाम लगाने के लिए, हम निरंतरता पुस्तकालय का उपयोग करते हैं।
reset { // Reset is a delimiter for continuations.
for { // Since the callback hell is relegated to continuation library machinery.
// a for-comprehension can be used
file1 <- shift(readFile(path1)) // shift has type (((A => B) => C) => A)
// We use it as (((Try[String] => Unit) => Unit) => Try[String])
// It takes all the code that occurs after it is called, up to the end of reset, and
// makes it into a closure of type (A => B).
// The reason this works is that shift is actually faking its return type.
// It only pretends to return A.
// It actually passes that closure into its function parameter (readFile(path1) here),
// And that function calls what it thinks is a normal callback with an A.
// And through compiler magic shift "injects" that A into its own callsite.
// So if readFile calls its callback with parameter Success("OK"),
// the shift is replaced with that value and the code is executed until the end of reset,
// and the return value of that is what the callback in readFile returns.
// If readFile called its callback twice, then the shift would run this code twice too.
// Since readFile returns Unit though, the type of the entire reset expression is Unit
//
// Think of shift as shifting all the code after it into a closure,
// and reset as resetting all those shifts and ending the closures.
file2 <- shift(readFile(path2))
} processFiles(file1, file2)
}
// After compilation, shift and reset are transformed back into closures
// The for comprehension first desugars to:
reset {
shift(readFile(path1)).flatMap { file1 => shift(readFile(path2)).foreach { file2 => processFiles(file1, file2) } }
}
// And then the callbacks are restored via CPS transformation
readFile(path1) { _.flatMap { file1 => // We see how shift moves the code after it into a closure
readFile(path2) { _.foreach { file2 =>
processFiles(file1, file2)
}}
}} // And we see how reset closes all those closures
// And it looks just like the old version!
ऐसे फंक्शन्स बनाना जो कंटीन्यू लेते हैं
यदि shift
को एक परिसीमन reset
ब्लॉक के बाहर कहा जाता है, तो इसका उपयोग उन कार्यों को बनाने के लिए किया जा सकता है जो स्वयं एक reset
ब्लॉक के अंदर निरंतरता बनाते हैं। यह ध्यान रखना महत्वपूर्ण है कि shift
का प्रकार सिर्फ (((A => B) => C) => A)
, यह वास्तव में है (((A => B) => C) => (A @cpsParam[B, C]))
। वह एनोटेशन चिह्न जहां सीपीएस परिवर्तन की आवश्यकता होती है। reset
बिना कॉल shift
reset
फ़ंक्शंस में उनकी एनोटेशन के साथ वापसी प्रकार "संक्रमित" होता है।
reset
ब्लॉक के अंदर, A @cpsParam[B, C]
मान A
मान है, हालांकि वास्तव में यह दिखावा है। गणना को पूरा करने के लिए जिस निरंतरता की आवश्यकता होती है, उसमें टाइप A => B
, इसलिए इस प्रकार रिटर्न करने वाली विधि का अनुसरण करने वाले कोड को B
वापस करना होगा। C
"वास्तविक" रिटर्न प्रकार है, और CPS परिवर्तन के बाद फ़ंक्शन कॉल में C
टाइप होता C
।
अब, उदाहरण, लाइब्रेरी के स्कैलाडॉक से लिया गया है
val sessions = new HashMap[UUID, Int=>Unit]
def ask(prompt: String): Int @suspendable = // alias for @cpsParam[Unit, Unit]. @cps[Unit] is also an alias. (@cps[A] = @cpsParam[A,A])
shift {
k: (Int => Unit) => {
println(prompt)
val id = uuidGen
sessions += id -> k
}
}
def go(): Unit = reset {
println("Welcome!")
val first = ask("Please give me a number") // Uses CPS just like shift
val second = ask("Please enter another number")
printf("The sum of your numbers is: %d\n", first + second)
}
यहां, ask
निरंतरता को एक नक्शे में संग्रहीत करेगा, और बाद में कुछ अन्य कोड उस "सत्र" को पुनः प्राप्त कर सकते हैं और उपयोगकर्ता को क्वेरी के परिणाम में पास कर सकते हैं। इस तरह, go
वास्तव में एक अतुल्यकालिक पुस्तकालय का उपयोग कर सकता है जबकि इसका कोड सामान्य अनिवार्यता कोड जैसा दिखता है।