Erlang 21 - 7. Types and Function Specifications

7 प्रकार और समारोह विनिर्देशों




erlang

7 प्रकार और समारोह विनिर्देशों

7.1 एर्लांग प्रकार की भाषा

Erlang एक गतिशील रूप से टाइप की जाने वाली भाषा है। फिर भी, यह एक विशेष प्रकार का निर्माण करने के लिए एर्लांग शब्दों के सेट घोषित करने के लिए एक संकेतन के साथ आता है। यह प्रभावी रूप से सभी एर्लांग शब्दों के सेट के विशिष्ट उपप्रकार बनाता है।

इसके बाद, इन प्रकारों का उपयोग रिकॉर्ड फ़ील्ड्स के प्रकारों को निर्दिष्ट करने के लिए किया जा सकता है और प्रकार के तर्क और रिटर्न प्रकार भी।

निम्नलिखित के लिए टाइप जानकारी का उपयोग किया जा सकता है:

  • फ़ंक्शन इंटरफेस को दस्तावेज़ करने के लिए
  • बग डिटेक्शन टूल के लिए अधिक जानकारी प्रदान करने के लिए, जैसे डायलाइज़र
  • विभिन्न रूपों के कार्यक्रम प्रलेखन उत्पन्न करने के लिए डॉक्यूमेंटेशन टूल जैसे कि ईडीओसी द्वारा शोषण किया जाना है

यह उम्मीद की जाती है कि इस खंड में वर्णित भाषा टाइप @type और @spec द्वारा उपयोग की जाने वाली शुद्ध टिप्पणी-आधारित @type और @spec घोषणाओं को प्रतिस्थापित करती है।

7.2 प्रकार और उनके सिंटैक्स

प्रकार Erlang शर्तों के सेट का वर्णन करते हैं। प्रकारों से मिलकर बनता है, और पूर्वनिर्धारित प्रकारों से बना होता है, उदाहरण के लिए, integer() , atom() , और pid() । पूर्वनिर्धारित प्रकार आम तौर पर इस प्रकार से संबंधित एरलंग शब्दों के अनंत सेट का प्रतिनिधित्व करते हैं। उदाहरण के लिए, प्रकार atom() सभी एरलंग परमाणुओं के सेट को दर्शाता है।

पूर्णांक और परमाणुओं के लिए, यह सिंगलटन प्रकारों के लिए अनुमत है; उदाहरण के लिए, पूर्णांक -1 और 42 , या परमाणु 'foo' और 'bar' । अन्य सभी प्रकारों को पूर्वनिर्धारित प्रकारों या सिंगलटन प्रकारों की यूनियनों का उपयोग करके बनाया गया है। एक प्रकार और उसके उप-प्रकारों के बीच एक प्रकार के संघ में, उपप्रकार को सुपरपाइप द्वारा अवशोषित किया जाता है। इस प्रकार, संघ को तब माना जाता है जैसे कि उपप्रकार संघ का घटक नहीं था। उदाहरण के लिए, टाइप यूनियन:

atom() | 'bar' | integer() | 42

प्रकार संघ के रूप में एक ही सेट का वर्णन करता है:

atom() | integer()

प्रकारों के बीच मौजूद उपप्रकार संबंधों के कारण, प्रकार एक जाली बनाते हैं जहां शीर्ष-सबसे तत्व, any() , सभी एरलैंग शब्दों के सेट को दर्शाता है और नीचे-सबसे तत्व को, none() , शब्दों के खाली सेट को दर्शाता है।

पूर्वनिर्धारित प्रकारों का प्रकार और प्रकारों के लिए वाक्य विन्यास इस प्रकार है:

Type :: any()                 %% The top type, the set of all Erlang terms
      | none()                %% The bottom type, contains no terms
      | pid()
      | port()
      | reference()
      | []                    %% nil
      | Atom
      | Bitstring
      | float()
      | Fun
      | Integer
      | List
      | Map
      | Tuple
      | Union
      | UserDefined           %% described in Type Declarations of User-Defined Types

Atom :: atom()
      | Erlang_Atom           %% 'foo', 'bar', ...

Bitstring :: <<>>
           | <<_:M>>          %% M is a positive integer
           | <<_:_*N>>        %% N is a positive integer
           | <<_:M, _:_*N>>

Fun :: fun()                  %% any function
     | fun((...) -> Type)     %% any arity, returning Type
     | fun(() -> Type)
     | fun((TList) -> Type)

Integer :: integer()
         | Erlang_Integer                    %% ..., -1, 0, 1, ... 42 ...
         | Erlang_Integer..Erlang_Integer    %% specifies an integer range

List :: list(Type)                           %% Proper list ([]-terminated)
      | maybe_improper_list(Type1, Type2)    %% Type1=contents, Type2=termination
      | nonempty_improper_list(Type1, Type2) %% Type1 and Type2 as above
      | nonempty_list(Type)                  %% Proper non-empty list

Map :: map()                                 %% denotes a map of any size
     | #{}                                   %% denotes the empty map
     | #{AssociationList}

Tuple :: tuple()                             %% denotes a tuple of any size
       | {}
       | {TList}

AssociationList :: Association
                 | Association, AssociationList

Association :: Type := Type                  %% denotes a mandatory association
             | Type => Type                  %% denotes an optional association

TList :: Type
       | Type, TList

Union :: Type1 | Type2

बिट स्ट्रिंग्स का सामान्य रूप है <<_:M, _:_*N>> , जहां M और N पॉजिटिव पूर्णांक हैं। यह एक बिट स्ट्रिंग को दर्शाता है जो M + (k*N) बिट्स लंबा है (जो कि एक बिट स्ट्रिंग है जो M बिट्स से शुरू होता है और N बिट्स के k सेगमेंट के साथ जारी रहता है, जहां k एक सकारात्मक पूर्णांक है)। नोटिफिकेशन <<_:_*N>> , <<_:M>> , और <<>> उन मामलों के लिए सुविधाजनक शॉर्टहैंड हैं जो M या N , या दोनों, शून्य हैं।

क्योंकि सूचियों का आमतौर पर उपयोग किया जाता है, उनके पास शॉर्टहैंड प्रकार के नोटेशन होते हैं। प्रकार list(T) और nonempty_list(T) में क्रमशः शॉर्टहैंड [T] और [T,...] होते हैं। दो शॉर्टहैंड के बीच एकमात्र अंतर यह है कि [T] एक खाली सूची हो सकती है [T,...] लेकिन [T,...] नहीं हो सकती।

ध्यान दें कि list() लिए आशुलिपि list() , अर्थात् अज्ञात प्रकार के तत्वों की सूची, [_] (या [any()] ), [] नहीं है। अंकन [] खाली सूची के लिए एकल प्रकार निर्दिष्ट करता है।

मानचित्र प्रकारों का सामान्य रूप #{AssociationList} AssociationList में प्रमुख प्रकारों को ओवरलैप करने की अनुमति दी जाती है, और यदि वे करते हैं, तो बाईं ओर एसोसिएशन पूर्वता लेता है। यदि यह इस प्रकार का है तो मैप एसोसिएशन के पास एसोसिएशनलिस्ट में एक कुंजी है। AssociationList में अनिवार्य और वैकल्पिक दोनों प्रकार के संघ शामिल हो सकते हैं। यदि एक एसोसिएशन प्रकार अनिवार्य है, तो उस प्रकार के साथ एक एसोसिएशन मौजूद होना चाहिए। वैकल्पिक एसोसिएशन प्रकार के मामले में यह कुंजी प्रकार के मौजूद होने के लिए आवश्यक नहीं है।

ध्यान दें कि map() का सिंटैक्टिक प्रतिनिधित्व #{any() => any()} (या #{_ => _} ) है, न कि #{} । अंकन #{} खाली मानचित्र के लिए सिंगलटन प्रकार निर्दिष्ट करता है।

सुविधा के लिए, निम्न प्रकार भी अंतर्निहित हैं। उन्हें टेबल में दिखाए गए प्रकार के यूनियनों के लिए पूर्वनिर्धारित उपनामों के रूप में सोचा जा सकता है।

निर्मित प्रकार के रूप में परिभाषित किया गया है
term() any()
binary() <<_:_*8>>
bitstring() <<_:_*1>>
boolean() 'false' | 'true'
byte() 0..255
char() 0..16#10ffff
nil() []
number() integer() | float()
list() [any()]
maybe_improper_list() maybe_improper_list(any(), any())
nonempty_list() nonempty_list(any())
string() [char()]
nonempty_string() [char(),...]
iodata() iolist() | binary()
iolist() maybe_improper_list(byte() | binary() | iolist(), binary() | [])
function() fun()
module() atom()
mfa() {module(),atom(),arity()}
arity() 0..255
identifier() pid() | port() | reference()
node() atom()
timeout() 'infinity' | non_neg_integer()
no_return() none()

तालिका 7.1: अंतर्निहित प्रकार, पूर्वनिर्धारित उपनाम

इसके अलावा, निम्नलिखित तीन अंतर्निहित प्रकार मौजूद हैं और नीचे परिभाषित के रूप में सोचा जा सकता है, हालांकि कड़ाई से उनकी "प्रकार की परिभाषा" ऊपर परिभाषित भाषा के अनुसार मान्य वाक्यविन्यास नहीं है।

निर्मित प्रकार वाक्य विन्यास द्वारा परिभाषित सोचा जा सकता है
non_neg_integer() 0..
pos_integer() 1..
neg_integer() ..-1

तालिका 7.2: अतिरिक्त अंतर्निहित प्रकार

उपयोगकर्ताओं को उन नामों के साथ प्रकार परिभाषित करने की अनुमति नहीं है जो पूर्वनिर्धारित या अंतर्निहित हैं। यह संकलक द्वारा जाँच की जाती है और इसके उल्लंघन के परिणामस्वरूप संकलन त्रुटि होती है।

ध्यान दें

निम्नलिखित अंतर्निहित सूची प्रकार भी मौजूद हैं, लेकिन उनका उपयोग शायद ही कभी होने की उम्मीद है। इसलिए, उनके लंबे नाम हैं:

nonempty_maybe_improper_list() :: nonempty_maybe_improper_list(any(), any())
nonempty_improper_list(Type1, Type2)
nonempty_maybe_improper_list(Type1, Type2)

जहाँ पिछले दो प्रकारों से निर्धारित होता है कि Erlang की शर्तों से किसी को उम्मीद होगी।

सुविधा के लिए, रिकॉर्ड संकेतन का उपयोग करने की अनुमति है। रिकॉर्ड्स संबंधित टुपल्स के लिए शॉर्टहैंड हैं:

Record :: #Erlang_Atom{}
        | #Erlang_Atom{Fields}

रिकॉर्ड संभवतः प्रकार की जानकारी रखने के लिए बढ़ाए जाते हैं। यह Type Information in Record Declarations में Type Information in Record Declarations वर्णित है।

7.3 उपयोगकर्ता-परिभाषित प्रकारों की घोषणा

जैसा कि देखा गया है, एक प्रकार का मूल सिंटैक्स एक परमाणु होता है जिसके बाद बंद कोष्ठक होता है। निम्नलिखित के रूप में- -type और -opaque विशेषताओं का उपयोग करके नए प्रकार घोषित किए जाते हैं:

-type my_struct_type() :: Type.
-opaque my_opaq_type() :: Type.

प्रकार का नाम परमाणु my_struct_type , इसके बाद कोष्ठक है। Type एक प्रकार है जैसा कि पिछले अनुभाग में परिभाषित किया गया है। एक वर्तमान प्रतिबंध यह है कि Type में केवल पूर्वनिर्धारित प्रकार या उपयोगकर्ता-परिभाषित प्रकार शामिल हो सकते हैं जो निम्नलिखित में से हैं:

  • मॉड्यूल-स्थानीय प्रकार, जो एक परिभाषा के साथ है जो मॉड्यूल के कोड में मौजूद है
  • दूरस्थ प्रकार, अर्थात्, अन्य मॉड्यूल द्वारा परिभाषित और निर्यात किया गया प्रकार; इसके बारे में जल्द ही।

मॉड्यूल-स्थानीय प्रकारों के लिए, मॉड्यूल में उनकी परिभाषा मौजूद संयोजक कंपाइलर द्वारा लागू की जाती है और एक संकलन त्रुटि के परिणामस्वरूप होती है। (वर्तमान में रिकॉर्ड के लिए एक समान प्रतिबंध मौजूद है।)

कोष्ठकों के बीच प्रकार चर को शामिल करके प्रकार की घोषणाओं को भी मानकीकृत किया जा सकता है। टाइप वैरिएबल्स का सिंटैक्स एरलांग वैरिएबल्स के समान होता है, जो कि ऊपरी केस अक्षर से शुरू होता है। स्वाभाविक रूप से, ये चर परिभाषा के आरएचएस पर प्रकट हो सकते हैं - और हैं। एक ठोस उदाहरण इस प्रकार है:

-type orddict(Key, Val) :: [{Key, Val}].

एक मॉड्यूल कुछ प्रकारों को यह घोषित करने के लिए निर्यात कर सकता है कि अन्य मॉड्यूल को दूरस्थ प्रकार के रूप में संदर्भित करने की अनुमति है। इस घोषणा के निम्नलिखित रूप हैं:

-export_type([T1/A1, ..., Tk/Ak]).

यहाँ टीआई के परमाणु (प्रकार का नाम) और एई उनके तर्क हैं

उदाहरण:

-export_type([my_struct_type/0, orddict/2]).

यह मानते हुए कि इन प्रकारों को मॉड्यूल 'mod' से निर्यात किया जाता है, आप निम्न जैसे दूरस्थ प्रकार के भावों का उपयोग करके अन्य मॉड्यूल से उन्हें संदर्भित कर सकते हैं:

mod:my_struct_type()
mod:orddict(atom(), term())

यह उन प्रकारों को संदर्भित करने की अनुमति नहीं है, जिन्हें निर्यात के रूप में घोषित नहीं किया गया है।

opaque रूप में घोषित प्रकार उन शब्दों के सेट का प्रतिनिधित्व करते हैं जिनकी संरचना उनके परिभाषित मॉड्यूल के बाहर से दिखाई नहीं देती है। यही है, केवल उन्हें परिभाषित करने वाले मॉड्यूल को उनके शब्द संरचना पर निर्भर करने की अनुमति है। नतीजतन, इस प्रकार के मॉड्यूल स्थानीय के रूप में ज्यादा मायने नहीं रखते हैं - मॉड्यूल स्थानीय प्रकार वैसे भी अन्य मॉड्यूल द्वारा सुलभ नहीं हैं - और हमेशा निर्यात किया जाना है।

7.4 रिकॉर्ड घोषणाओं में जानकारी टाइप करें

रिकॉर्ड की घोषणा में रिकॉर्ड फ़ील्ड के प्रकार निर्दिष्ट किए जा सकते हैं। इसके लिए वाक्य रचना इस प्रकार है:

-record(rec, {field1 :: Type1, field2, field3 :: Type3}).

प्रकार एनोटेशन वाले क्षेत्रों के लिए, उनका प्रकार किसी भी प्रकार की चूक () के लिए है। यह है कि, पिछला उदाहरण निम्नलिखित के लिए एक आशुलिपि है:

-record(rec, {field1 :: Type1, field2 :: any(), field3 :: Type3}).

फ़ील्ड के लिए प्रारंभिक मानों की उपस्थिति में, प्रकार को आरंभीकरण के बाद घोषित किया जाना चाहिए, इस प्रकार है:

-record(rec, {field1 = [] :: Type1, field2, field3 = 42 :: Type3}).

फ़ील्ड्स के लिए प्रारंभिक मान संगत प्रकारों के साथ (यानी, एक सदस्य) हैं। यह संकलक द्वारा जाँच की जाती है और एक उल्लंघन का पता चलने पर एक संकलन त्रुटि में परिणाम करता है।

ध्यान दें

अर्लंग / ओटीपी 19 से पहले, प्रारंभिक मूल्यों के बिना फ़ील्ड के लिए, सिंगलटन प्रकार 'undefined' को सभी घोषित प्रकारों में जोड़ा गया था। दूसरे शब्दों में, निम्नलिखित दो रिकॉर्ड घोषणाओं पर समान प्रभाव पड़ा:

-record(rec, {f1 = 42 :: integer(),
              f2      :: float(),
              f3      :: 'a' | 'b'}).

-record(rec, {f1 = 42 :: integer(),
              f2      :: 'undefined' | float(),
              f3      :: 'undefined' | 'a' | 'b'}).

यह अब मामला ही नहीं है। यदि आपको अपने रिकॉर्ड फ़ील्ड प्रकार में 'undefined' आवश्यकता है, तो आपको इसे स्पष्ट रूप से टाइपस्पेस में जोड़ना होगा, जैसा कि 2 के उदाहरण में है।

किसी भी रिकॉर्ड, प्रकार की जानकारी या नहीं, एक बार परिभाषित करने के बाद, निम्न सिंटैक्स का उपयोग करके एक प्रकार के रूप में इस्तेमाल किया जा सकता है:

#rec{}

इसके अलावा, रिकॉर्ड फ़ील्ड को तब निर्दिष्ट किया जा सकता है जब फ़ील्ड के बारे में निम्न प्रकार की जानकारी जोड़कर रिकॉर्ड प्रकार का उपयोग किया जाता है:

#rec{some_field :: Type}

किसी भी अनिर्दिष्ट क्षेत्रों को मूल रिकॉर्ड घोषणा में प्रकार माना जाता है।

7.5 कार्य के लिए विनिर्देश

फ़ंक्शन के लिए एक विनिर्देश (या अनुबंध) -spec विशेषता का उपयोग करके दिया जाता है। सामान्य प्रारूप निम्नानुसार है:

-spec Module:Function(ArgType1, ..., ArgTypeN) -> ReturnType.

फ़ंक्शन की समता को तर्कों की संख्या से मेल खाना चाहिए, अन्यथा एक संकलन त्रुटि होती है।

इस प्रपत्र का उपयोग शीर्ष लेख फ़ाइलों (.hrl) में भी किया जा सकता है ताकि निर्यात कार्यों के लिए प्रकार की जानकारी घोषित की जा सके। फिर इन हेडर फ़ाइलों को उन फ़ाइलों में शामिल किया जा सकता है जो (स्पष्ट या स्पष्ट रूप से) इन कार्यों को आयात करते हैं।

किसी दिए गए मॉड्यूल के भीतर, ज्यादातर मामलों में निम्नलिखित आशुलिपि होती है:

-spec Function(ArgType1, ..., ArgTypeN) -> ReturnType.

इसके अलावा, प्रलेखन उद्देश्यों के लिए, तर्क नाम दिए जा सकते हैं:

-spec Function(ArgName1 :: Type1, ..., ArgNameN :: TypeN) -> RT.

एक फंक्शन स्पेसिफिकेशन को ओवरलोड किया जा सकता है। अर्थात्, यह कई प्रकार के हो सकते हैं, एक अर्धविराम ( ; ) द्वारा अलग किए गए:

-spec foo(T1, T2) -> T3
       ; (T4, T5) -> T6.

एक वर्तमान प्रतिबंध, जो वर्तमान में संकलक द्वारा एक चेतावनी (त्रुटि नहीं) में परिणत होता है, यह है कि तर्क प्रकार के डोमेन ओवरलैप होते हैं। उदाहरण के लिए, एक चेतावनी में निम्नलिखित विनिर्देश परिणाम हैं:

-spec foo(pos_integer()) -> pos_integer()
       ; (integer()) -> integer().

फ़ंक्शन के इनपुट और आउटपुट तर्कों के लिए संबंध निर्दिष्ट करने के लिए प्रकार चर का उपयोग विनिर्देशों में किया जा सकता है। उदाहरण के लिए, निम्न विनिर्देश एक बहुरूपी पहचान समारोह के प्रकार को परिभाषित करता है:

-spec id(X) -> X.

ध्यान दें कि उपरोक्त विनिर्देश किसी भी तरह से इनपुट और आउटपुट प्रकार को प्रतिबंधित नहीं करता है। ये प्रकार गार्ड की तरह उप-प्रकार की बाधाओं से बाध्य हो सकते हैं और बंधे हुए परिमाण प्रदान कर सकते हैं:

-spec id(X) -> X when X :: tuple().

वर्तमान में, :: बाधा («के रूप में« का एक उपप्रकार पढ़ा जाता है) एकमात्र गार्ड बाधा है जिसका उपयोग किसी -spec विशेषता के हिस्से में किया जा सकता है।

ध्यान दें

उपरोक्त फ़ंक्शन विनिर्देश एक ही प्रकार के चर की कई घटनाओं का उपयोग करता है। निम्नलिखित फ़ंक्शन विनिर्देश से अधिक प्रकार की जानकारी प्रदान करता है, जहां प्रकार चर गायब हैं:

-spec id(tuple()) -> tuple().

बाद के विनिर्देश का कहना है कि फ़ंक्शन कुछ टपल लेता है और कुछ टपल देता है। X प्रकार चर के साथ विनिर्देश निर्दिष्ट करता है कि फ़ंक्शन एक टपल लेता है और एक ही टपल लौटाता है

हालाँकि, यह उन उपकरणों पर निर्भर है जो विशिष्टताओं को चुनने के लिए विनिर्देशों को संसाधित करते हैं या नहीं इस अतिरिक्त जानकारी को ध्यान में रखें।

एक :: बाधा का दायरा है (...) -> RetType विनिर्देश जिसके बाद यह प्रकट होता है। भ्रम से बचने के लिए, यह सुझाव दिया जाता है कि एक ओवरलोड अनुबंध के विभिन्न घटकों में विभिन्न चर का उपयोग किया जाता है, जैसा कि निम्नलिखित उदाहरण में दिखाया गया है:

-spec foo({X, integer()}) -> X when X :: atom()
       ; ([Y]) -> Y when Y :: number().

एर्लांग में कुछ कार्य वापस लौटने के लिए नहीं हैं; या तो क्योंकि वे सर्वर को परिभाषित करते हैं या क्योंकि उनका उपयोग अपवादों को फेंकने के लिए किया जाता है, जैसा कि निम्नलिखित फ़ंक्शन में है:

my_error(Err) -> erlang:throw({error, Err}).

ऐसे कार्यों के लिए, निम्नलिखित के अनुबंध के माध्यम से, उनके "वापसी" के लिए विशेष no_return() प्रकार का उपयोग करने की सिफारिश की जाती है:

-spec my_error(term()) -> no_return().