क्या कोई मुझ पर निर्भर टाइपिंग समझा सकता है? मेरे पास हास्केल, केयेन, एपिग्राम या अन्य कार्यात्मक भाषाओं में बहुत कम अनुभव है, इसलिए आप जिन शब्दों का उपयोग कर सकते हैं, वे जितना सरल हैं, मैं उसकी सराहना करूंगा!
क्या कोई मुझ पर निर्भर टाइपिंग समझा सकता है? मेरे पास हास्केल, केयेन, एपिग्राम या अन्य कार्यात्मक भाषाओं में बहुत कम अनुभव है, इसलिए आप जिन शब्दों का उपयोग कर सकते हैं, वे जितना सरल हैं, मैं उसकी सराहना करूंगा!
जवाबों:
इस पर विचार करें: सभी सभ्य प्रोग्रामिंग भाषाओं में आप फ़ंक्शन लिख सकते हैं, जैसे
def f(arg) = result
यहां, f
एक मूल्य लेता है arg
और एक मूल्य की गणना करता है result
। यह मूल्यों से मूल्यों तक का कार्य है।
अब, कुछ भाषाएँ आपको बहुरूपता (उर्फ़ सामान्य) मान परिभाषित करने की अनुमति देती हैं:
def empty<T> = new List<T>()
यहां, empty
एक प्रकार लेता है T
और एक मूल्य की गणना करता है। यह एक प्रकार से मानों का कार्य है।
आमतौर पर, आप सामान्य प्रकार की परिभाषाएँ भी दे सकते हैं:
type Matrix<T> = List<List<T>>
यह परिभाषा एक प्रकार लेता है और यह एक प्रकार देता है। इसे एक प्रकार के प्रकार से एक फ़ंक्शन के रूप में देखा जा सकता है।
आम भाषाओं के लिए बहुत कुछ। एक भाषा को भरोसेमंद रूप से टाइप किया जाता है यदि यह मानों से प्रकारों के कार्यों को परिभाषित करते हुए, 4 वीं संभावना भी प्रदान करता है। या दूसरे शब्दों में, मान पर एक प्रकार की परिभाषा को पैरामीटर:
type BoundedInt(n) = {i:Int | i<=n}
कुछ मुख्यधारा की भाषाओं के कुछ नकली रूप हैं जिन्हें भ्रमित नहीं करना है। उदाहरण के लिए C ++ में, टेम्प्लेट पैरामीटर के रूप में मान ले सकते हैं, लेकिन उन्हें लागू होने पर संकलन-समय स्थिरांक होना चाहिए। वास्तव में निर्भर टाइप की भाषा में ऐसा नहीं है। उदाहरण के लिए, मैं ऊपर दिए गए प्रकार का उपयोग कर सकता हूं:
def min(i : Int, j : Int) : BoundedInt(j) =
if i < j then i else j
यहां, फ़ंक्शन का परिणाम प्रकार वास्तविक तर्क मान पर निर्भर करता हैj
, इस प्रकार शब्दावली।
BoundedInt
उदाहरण वास्तव में एक शोधन प्रकार, हालांकि? यह 'बहुत करीब' है, लेकिन वास्तव में 'निर्भर प्रकार' की तरह नहीं है, उदाहरण के लिए, इदरिस ने सबसे पहले dep.typing के बारे में एक ट्यूटोरियल में उल्लेख किया है।
आश्रित प्रकार, तर्क त्रुटियों के बड़े सेट को संकलन समय पर समाप्त करने में सक्षम बनाते हैं । इसे समझने के लिए फ़ंक्शन पर निम्नलिखित विनिर्देश पर विचार करें f
:
फ़ंक्शन को इनपुट के रूप में
f
केवल पूर्णांक भी लेना चाहिए ।
निर्भर प्रकारों के बिना आप ऐसा कुछ कर सकते हैं:
def f(n: Integer) := {
if n mod 2 != 0 then
throw RuntimeException
else
// do something with n
}
यहां कंपाइलर यह पता नहीं लगा सकता कि n
क्या वास्तव में भी है, यानी कंपाइलर के दृष्टिकोण से निम्नलिखित अभिव्यक्ति ठीक है:
f(1) // compiles OK despite being a logic error!
यह प्रोग्राम रन करेगा और फिर रनटाइम पर अपवाद को फेंक देगा, अर्थात, आपके प्रोग्राम में लॉजिक एरर है।
अब, निर्भर प्रकार आपको अधिक अभिव्यंजक बनाने में सक्षम बनाते हैं और आपको कुछ इस तरह लिखने में सक्षम करेंगे:
def f(n: {n: Integer | n mod 2 == 0}) := {
// do something with n
}
यहाँ n
निर्भर प्रकार है {n: Integer | n mod 2 == 0}
। यह इस रूप में जोर से पढ़ने में मदद कर सकता है
n
पूर्णांकों के सेट का एक सदस्य ऐसा है कि प्रत्येक पूर्णांक 2 से विभाज्य है।
इस मामले में कंपाइलर एक लॉजिक एरर के कंपाइल टाइम में पता लगाएगा कि आपने कहां से एक विषम संख्या पास की है f
और प्रोग्राम को पहली बार में निष्पादित करने से रोकेगा:
f(1) // compiler error
यहाँ स्केल पथ-निर्भर प्रकारों का उपयोग करके एक उदाहरण दिया गया है कि हम f
इस तरह की आवश्यकता को पूरा करते हुए फ़ंक्शन को लागू करने का प्रयास कैसे कर सकते हैं:
case class Integer(v: Int) {
object IsEven { require(v % 2 == 0) }
object IsOdd { require(v % 2 != 0) }
}
def f(n: Integer)(implicit proof: n.IsEven.type) = {
// do something with n safe in the knowledge it is even
}
val `42` = Integer(42)
implicit val proof42IsEven = `42`.IsEven
val `1` = Integer(1)
implicit val proof1IsOdd = `1`.IsOdd
f(`42`) // OK
f(`1`) // compile-time error
कैसे मूल्य कुंजी जानकारी में है n
मूल्य के प्रकार में प्रकट होता है proof
अर्थात् n.IsEven.type
:
def f(n: Integer)(implicit proof: n.IsEven.type)
^ ^
| |
value value
हम कहते हैं कि प्रकार मूल्यn.IsEven.type
पर निर्भर करता है इसलिए शब्द निर्भर-प्रकार । n
f(random())
संकलन में त्रुटि होगी?
f
कुछ अभिव्यक्ति को लागू करने के लिए संकलक (आपकी मदद के बिना या बिना) की आवश्यकता होगी, ताकि अभिव्यक्ति हमेशा भी हो, और ऐसा कोई प्रमाण मौजूद नहीं है random()
(क्योंकि यह वास्तव में विषम हो सकता है), इसलिए f(random())
संकलन करने में विफल होगा।
यदि आप C ++ को जानते हैं तो यह एक प्रेरक उदाहरण प्रदान करना आसान है:
मान लीजिए कि हमारे पास कुछ कंटेनर प्रकार और दो उदाहरण हैं
typedef std::map<int,int> IIMap;
IIMap foo;
IIMap bar;
और इस कोड के टुकड़े पर विचार करें (आप मान सकते हैं कि फू गैर-खाली है):
IIMap::iterator i = foo.begin();
bar.erase(i);
यह स्पष्ट कचरा है (और संभवतः डेटा संरचनाओं को दूषित करता है), लेकिन यह "इटूमीटर इन फू" और "इट्रेटर इन बार" के बाद से ठीक-ठीक टाइप करेगा। IIMap::iterator
, भले ही वे पूरी तरह से असंगत हैं।
मुद्दा यह है कि एक इट्रेटर प्रकार केवल कंटेनर प्रकार पर निर्भर नहीं होना चाहिए, लेकिन वास्तव में कंटेनर ऑब्जेक्ट पर , अर्थात इसे "गैर-स्थिर सदस्य प्रकार" होना चाहिए:
foo.iterator i = foo.begin();
bar.erase(i); // ERROR: bar.iterator argument expected
इस तरह की सुविधा, एक प्रकार (foo.iterator) को व्यक्त करने की क्षमता जो एक शब्द (फू) पर निर्भर करती है, वास्तव में निर्भरता का अर्थ है।
आप अक्सर इस सुविधा को नहीं देख पाने का कारण यह है कि यह कीड़े के एक बड़े डिब्बे को खोलता है: आप अचानक उन स्थितियों में समाप्त हो जाते हैं जहां, संकलन-समय पर जांच करने के लिए कि क्या दो प्रकार समान हैं, आप दो अभिव्यक्तियों को साबित करने के लिए समाप्त होते हैं समतुल्य हैं (हमेशा रनटाइम पर समान मान प्राप्त करेंगे)। परिणामस्वरूप, यदि आप विकिपीडिया की सूची की तुलना भरोसेमंद रूप से टाइप की गई भाषाओं की अपनी प्रमेय की सूची से करते हैं, तो यह साबित करता है कि आपको संदिग्ध मानसिकता दिखाई दे सकती है। ;-)
पुस्तक के प्रकार और प्रोग्रामिंग भाषाएँ उद्धृत करना (30.5):
इस पुस्तक का अधिकांश विभिन्न प्रकार के अमूर्त तंत्र को औपचारिक बनाने से संबंधित है। बस टाइप किए गए लैम्ब्डा-कैलकुलस में, हमने एक शब्द लेने और एक सबटेरम को अमूर्त करने के संचालन को औपचारिक रूप दिया, एक फ़ंक्शन को उत्पन्न किया जो बाद में इसे अलग-अलग शब्दों में लागू करके त्वरित किया जा सकता है। सिस्टम में , हमने टाइप किए गए लैम्ब्डा-कैलकुलस "एक स्तर ऊपर," के प्रकारों को फिर से टाइप किया और एक टाइप ऑपरेटर को प्राप्त करने के लिए एक उपप्रकार को बाहर निकाला और बाद में इसे विभिन्न प्रकारों में लागू करके त्वरित किया जा सकता है। अमूर्त के इन सभी रूपों के सोचने का एक सुविधाजनक तरीका अभिव्यक्ति के परिवारों के संदर्भ में है, जो अन्य अभिव्यक्तियों द्वारा अनुक्रमित है। एक साधारण लंबोदर अमूर्त शब्दों का एक परिवार है जिसे शब्दों द्वारा अनुक्रमित किया जाता है । इसी प्रकार, एक प्रकार का अमूर्त
F
, हमने एक शब्द लेने और एक प्रकार से अमूर्त करने के संचालन पर विचार किया, एक शब्द की उपज जो इसे विभिन्न प्रकारों पर लागू करके त्वरित किया जा सकता है। मेंλω
λx:T1.t2
[x -> s]t1
s
λX::K1.t2
प्रकारों द्वारा अनुक्रमित शर्तों का एक परिवार है, और एक प्रकार का ऑपरेटर प्रकारों द्वारा अनुक्रमित प्रकारों का एक परिवार है।
λx:T1.t2
शर्तों द्वारा अनुक्रमित परिवार
λX::K1.t2
प्रकारों द्वारा अनुक्रमित शब्दों का परिवार
λX::K1.T2
प्रकारों द्वारा अनुक्रमित प्रकार के परिवारइस सूची को देखते हुए, यह स्पष्ट है कि एक संभावना है जिसे हमने अभी तक नहीं माना है: शर्तों द्वारा अनुक्रमित प्रकार के परिवार। निर्भरता के प्रकार के तहत, अमूर्त के इस रूप का भी बड़े पैमाने पर अध्ययन किया गया है।