डायलाइज़र चश्मा: जेडी का रास्ता

Erlang और elixir का उपयोग करने वाले दो प्रकार के डेवलपर हैं: वे जो Dialyzer के लिए चश्मा लिखते हैं, और जो अभी तक नहीं हैं। पहले ऐसा लगता है कि यह सब समय की बर्बादी है, खासकर उन लोगों के लिए जो ढीली टाइपिंग वाली भाषाओं से आते हैं। हालांकि, उन्होंने मुझे सीआई चरण से पहले एक से अधिक गलती पकड़ने में मदद की, और - जल्दी या बाद में - कोई भी डेवलपर समझता है कि उन्हें जरूरत है; न केवल अर्ध-सख्त टाइपिंग के लिए एक मार्गदर्शन उपकरण के रूप में, बल्कि कोडिंग दस्तावेज़ में भी बहुत मदद मिलती है।


लेकिन, जैसा कि हमारी क्रूर दुनिया में हमेशा होता है, किसी भी बैरल में, आप चम्मच के बिना नहीं कर सकते। संक्षेप में, @spec निर्देश फ़ंक्शन घोषणा कोड को डुप्लिकेट करता है। नीचे मैं आपको बताऊंगा कि कोड की बीस लाइनें फॉर्म के डिज़ाइन में किसी फ़ंक्शन के विनिर्देश और घोषणा को संयोजित करने में कैसे मदद करेंगी


 defs is_forty_two(n: integer) :: boolean do n == 42 end 

जैसा कि आप जानते हैं, अमृत में मैक्रों के अलावा कुछ नहीं है। यहां तक ​​कि Kernel.defmacro/2 एक । इसलिए, हमें केवल इतना करना चाहिए कि हम अपने स्वयं के मैक्रो को परिभाषित करें, जो ऊपर दिए गए निर्माण से कल्पना और फ़ंक्शन दोनों की घोषणा करेगा।


अच्छा, चलिए शुरू करते हैं।


चरण 1. स्थिति का अध्ययन करें।


शुरू करने के लिए, हम समझेंगे कि किस तरह का एएसटी हमारे मैक्रो को तर्कों के रूप में प्राप्त होगा।


 defmodule CustomSpec do defmacro defs(args, do: block) do IO.inspect(args) :ok end end defmodule CustomSpec.Test do import CustomSpec defs is_forty_two(n: integer) :: boolean do n == 42 end end 

यहां फॉर्मेटर विद्रोह करेगा, कोष्ठक जोड़ेगा और उनके अंदर कोड को प्रारूपित करेगा ताकि आंखों से आंसू बहें। इस से उसे वीन करो। इस तरह .formatter.exs कॉन्फ़िगरेशन .formatter.exs बदलें:


 [ inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"], export: [locals_without_parens: [defs: 2]] ] 

चलो अपनी भेड़ों के पास वापस जाते हैं और देखते हैं कि वहाँ पर defs/2 क्या मिलता है। यह ध्यान दिया जाना चाहिए कि हमारा IO.inspect/2 संकलन चरण पर काम IO.inspect/2 (यदि आप यह नहीं समझते हैं कि, आपको अभी तक मैक्रोज़ के साथ खेलने की आवश्यकता नहीं है, तो शानदार क्रिस मैककॉर्ड की मेट्रोपोग्रामिंग एलिक्सबुक पुस्तक पढ़ें)। ताकि संकलक कसम न खाए, हम लौटते हैं :ok (मैक्रोज़ को सही एएसटी वापस करना होगा)। तो:


 {:"::", [line: 7], [ {:is_forty_two, [line: 7], [[n: {:integer, [line: 7], nil}]]}, {:boolean, [line: 7], nil} ]} 

उह हुह। पार्सर मानता है कि यहां मुख्य बात :: ऑपरेटर है, जो फ़ंक्शन परिभाषा और रिटर्न प्रकार को glues करता है। फ़ंक्शन परिभाषा में Keyword , "पैरामीटर नाम → प्रकार" के रूप में मापदंडों की एक सूची है।


चरण 2. तेजी से विफल।


चूंकि हमने अभी तक केवल इस तरह के कॉल सिंटैक्स का समर्थन करने का फैसला किया है, इसलिए हमें defs मैक्रो की परिभाषा को फिर से लिखना होगा ताकि यदि, उदाहरण के लिए, वापसी प्रकार निर्दिष्ट न हो, तो कंपाइलर तुरंत शपथ ले।


 defmacro defs({:"::", _, [{fun, _, [args_spec]}, {ret_spec, _, nil}]}, do: block) do 

खैर, इसे लागू करने का समय आ गया है।


चरण 3. ऐनक और फ़ंक्शन घोषणाओं को उत्पन्न करें।


 defmodule CustomSpec do defmacro defs({:"::", _, [{fun, _, [args_spec]}, {ret_spec, _, nil}]}, do: block) do #     args = for {arg, _spec} <- args_spec, do: Macro.var(arg, nil) #    args_spec = for {_arg, spec} <- args_spec, do: Macro.var(spec, nil) quote do @spec unquote(fun)(unquote_splicing(args_spec)) :: unquote(ret_spec) def unquote(fun)(unquote_splicing(args)) do unquote(block) end end end end 

यहां सब कुछ इतना पारदर्शी है कि इस पर टिप्पणी करने के लिए भी कुछ नहीं है।


यह देखने का समय है कि CustomSpec.Test.is_forty_two(42) कॉल करने से क्या होगा:


 iex> CustomSpec.Test.is_forty_two 42 #⇒ true iex> CustomSpec.Test.is_forty_two 43 #⇒ false 

खैर, यह काम करता है।


चरण 4. क्या वह सब है?


नहीं, बिल्कुल। वास्तविक जीवन में, आपको कई अलग-अलग डिफ़ॉल्ट मापदंडों के साथ कार्यों के लिए अमान्य कॉल, हेडर परिभाषाओं को सही ढंग से संभालना होगा, ध्यान से युक्ति को इकट्ठा करना होगा, चर नामों के साथ, सुनिश्चित करें कि सभी तर्क नाम अलग-अलग और बहुत कुछ हैं। लेकिन प्रदर्शन के प्रमाण के रूप में करेंगे।


सिद्धांत रूप में, आप अभी भी सहकर्मियों को कुछ इस तरह आश्चर्यचकित कर सकते हैं:


 defmodule CustomSpec do defmacro __using__(_) do import Kernel, except: [def: 2] import CustomSpec defmacro def(args, do: block) do defs(args, do: block) end end ... end 

(अभी भी defs/2 को ठीक करना होगा, Kernel.def बजाय Kernel.def बनाना), लेकिन मैं ऐसा करने के लिए दृढ़ता से अनुशंसा करूंगा।


आपका ध्यान, स्थूल स्वास्थ्य के लिए धन्यवाद!

Source: https://habr.com/ru/post/hi480466/


All Articles