- Elixir के guard में
orconditions का क्रम बदलने भर से, एक जैसे logical expression जैसा दिखने वाले code का result बदल सकता है is_integer(x) or is_map_key(x,:foo)क्रम में integer input पर short-circuit evaluation पहले हो जाती है और risky check skip हो जाता है- इसके उलट
is_map_key(x,:foo) or is_integer(x)में integer input पर पहला conditionfalseलौटाने के बजाय fail हो जाता है, इसलिए दूसरा condition evaluate ही नहीं होता - इसी फर्क की वजह से
Foo.a(%{foo: 21}),Foo.a(37),Foo.b(%{foo: 21})तोtrueहैं, लेकिनFoo.b(37)falseबन जाता है - यह boolean operation के commutative law के टूटने जैसा लग सकता है, लेकिन short-circuit evaluation वाले
orमें condition order का असर स्वाभाविक है, और Elixir 1.20.1 व OTP 29 के आधार पर कोई warning नहीं दिखती
condition order से result बदलने का उदाहरण
- example module
Fooमेंa/1औरb/1दो functions define किए गए हैंa/1: guard कोis_integer(x) or is_map_key(x, :foo)क्रम में check करता हैb/1: guard कोis_map_key(x, :foo) or is_integer(x)क्रम में check करता है- guard match होने पर
true, नहीं तो अगले clause मेंfalsereturn होता है
-
a/1: जब safe condition पहले आती हैFoo.a(%{foo: 21})trueबनता हैis_integer(x)falseहैis_map_key(x, :foo)trueहैorका resulttrueहोने से पहला clause match हो जाता है
Foo.a(37)भीtrueबनता हैis_integer(x)trueहैorकी short-circuit evaluation होने सेis_map_key(x, :foo)execute नहीं होता
-
b/1: जब fail हो सकने वाला condition पहले आता हैFoo.b(%{foo: 21})trueबनता हैis_map_key(x, :foo)trueहै- बाद वाला
is_integer(x)execute नहीं होता
Foo.b(37)falseबनता है- पहला condition
is_map_key(x, :foo)falsereturn करने के बजाय fail हो जाता है - guard function का fail होना
falseमें convert नहीं होता, बल्कि पूरे guard expression को fail कर देता है - बाद वाला
is_integer(x)call नहीं होता और पहला clause भी match नहीं करता
- पहला condition
short-circuit evaluation और warning का न होना
- कई Elixir developers को यह behavior boolean operator के commutative law के टूटने जैसा लग सकता है
- लेकिन
orshort-circuit evaluation करता है, इसलिए दोनों conditions की जगह बदलने पर हमेशा एक जैसा result आएगा, ऐसा मानना सही नहीं है - संदर्भ environment Elixir 1.20.1, OTP 29 है, और इस issue पर Elixir कोई warning नहीं देता, ऐसा दिखता है
1 टिप्पणियां
Lobste.rs की राय
मैं Elixir प्रोग्रामर नहीं हूं, लेकिन आखिरी उदाहरण में सबसे चौंकाने वाली बात यह है कि guard expression की error caller तक propagate नहीं होती, बल्कि वह guard “skip” हो जाता है।
मुझे लगता है समझ में आता है कि इसे ऐसा क्यों बनाया गया, लेकिन यह भी हैरानी की बात नहीं कि इससे intuition के उलट नतीजे निकलते हैं।
यह सोचें तो irony है कि Erlang का API design Armstrong की Erlang thesis p109/s4.5 में बताई गई intentional programming में मदद करने के लिए था।
thesis में
dict:fetch(Key, Dict),dict:search(Key, Dict),dict:is_key(Key, Dict)जैसे functions को अलग-अलग समझाया गया है, जो programmer का intent दिखाते हैं: “key जरूर होनी चाहिए”, “हो भी सकती है इसलिए flow अलग करें”, और “सिर्फ मौजूदगी check करनी है।”लेकिन Elixir का
is_map_key/2अगर “dict” argument dict नहीं है तो exception देता है, और वह exception failure पूरे guard clause की failure में बदल जाता है, जिससे यह distinction टूटती हुई लगती है।उल्टा, अगर कोई भाषा ऐसी हो जहां
orexception को पकड़करfalseमें मिला दे, तो शायद दूसरे cases में वह और भी ज्यादा चौंकाने वाला लगे।is_map_key/2असल में पूरी तरह सामान्य Erlang function है।https://www.erlang.org/doc/apps/erts/erlang.html#is_map_key/2
पहले देखी गई इस चर्चा की वजह से मैं इस quiz को हल करने के लिए तैयार था, और तब मैंने कुछ चीजें सीखी थीं।
सीखने को मिला, लेकिन यह अफसोस रहा कि Pratchett reference से क्यों बचा गया।
Death कहीं न कहीं अपना माथा पकड़ रहा होगा।
यहां दो दिलचस्प बातें हैं:
falseनहीं, बल्कि failed guard पूरे expression को fail करा देता है; और थोड़े non-intuitive तरीके सेis_map_key,is_mapcheck को अपने अंदर include नहीं करता।is_map(x) and is_map_key(x, :corporal)जैसा तीसरा variant जोड़ें तो यह उम्मीद के मुताबिक काम करता है।is_map_keyका behavior थोड़ा inconsistent लगता है और इसलिए surprising महसूस होता है; बाकीis_...guards में से कौन safe हैं और किनमें type expectation मानकर evaluation करनी पड़ती है, यह check करना दिलचस्प होगा।is_map_keyही एकमात्रis_guard है जिसे किसी खास तरह का argument चाहिए।बाकी
is_functions boolean nature रखते हैं और हमेशाtrue | falseलौटाते हैं, fail नहीं होते।यहां एक दिलचस्प Elixir style सवाल उठता है।
उदाहरण मजेदार है और explanation भी अच्छी है, लेकिन व्यक्तिगत रूप से मैं जहां संभव हो guards के बजाय pattern matching को prefer करता हूं।
बेशक exceptions हैं, पर ऐसे functions मैं आमतौर पर कई function clauses के रूप में लिखता, जैसे
def a(%{foo: _x}), do: true,def a(x) when is_integer(x), do: true,def a(_), do: false।साथ में देखने लायक: https://learnyouahaskell.github.io/syntax-in-functions.html/…
Haskell में guard के अंदर arbitrary function call कर सकते हैं, लेकिन Erlang उसमें allowed functions के set को restrict करता है।