- यह लेख इंटरव्यू की स्थिति में एक उम्मीदवार द्वारा साधारण FizzBuzz समस्या को lambda calculus-आधारित pure function combinators से लागू करने की व्यंग्यात्मक कथा प्रस्तुत करता है
- नायक JavaScript से शुरुआत करता है, लेकिन धीरे-धीरे S, K, I combinators को परिभाषित करते हुए भाषा की बुनियादी संरचना को खुद ही फिर से निर्मित करता है
- इसके बाद वह boolean, numbers, lists, strings सबको केवल combinators से व्यक्त करता है, और Y combinator के जरिए recursion लागू करता है
- अंततः वह FizzBuzz को पूरी तरह point-free शैली में पूरा करता है, लेकिन कोड इतना फैल जाता है कि मनुष्यों के समझने से परे हो जाता है
- यह लेख प्रोग्रामिंग भाषाओं के सार और abstraction की चरम सीमा को हास्यपूर्ण ढंग से खंगालता है और डेवलपर संस्कृति के आत्म-व्यंग्य को उजागर करता है
इंटरव्यू की शुरुआत: FizzBuzz और combinators
- कहानी की शुरुआत उस दृश्य से होती है जहाँ इंटरव्यूअर Dana उम्मीदवार से FizzBuzz समस्या हल करने को कहती हैं
- उम्मीदवार सामान्य loop की जगह S और K combinators को परिभाषित करते हुए समस्या हल करना शुरू करता है
- Dana हैरान होती हैं, लेकिन उम्मीदवार कहता है, “बस थोड़ा और करना है,” और आगे बढ़ता रहता है
- उम्मीदवार I, B, C, W, T जैसे कई combinators को परिभाषित करता है और इन्हें एक नई भाषा के बुनियादी घटकों की तरह इस्तेमाल करता है
- हर combinator function composition, argument swapping, self-application जैसे lambda calculus के मुख्य विचारों को लागू करता है
- कोड टिप्पणियों में “Bluebird, Cardinal, Warbler, Thrush” जैसे combinator नामों के उपनाम भी दिखाई देते हैं
boolean और numbers की परिभाषा
- उम्मीदवार TRUE, FALSE, NOT को सिर्फ combinators से परिभाषित कर boolean logic तैयार करता है
- Dana पूछती हैं, “क्या यह lambda calculus है?”, लेकिन उम्मीदवार जवाब देता है, “lambda calculus बहुत भारी-भरकम है।”
- इसके बाद वह ZERO, SUCC, PRED, IS_ZERO आदि को परिभाषित कर number system (Church numeral) बनाता है
SUCC successor, PRED predecessor, और DECREMENT ऐसा decrement operation लागू करता है जो 0 से नीचे नहीं जाता
ONE से TEN तक की संख्याएँ क्रमवार परिभाषित की जाती हैं
recursion और Y combinator
- उम्मीदवार ADD function को परिभाषित करता है और फिर उसे धीरे-धीरे point-free रूप में बदल देता है
- वह
ADD_MAKER को परिभाषित करता है और recursion संभव करने के लिए उसे Y combinator में लपेट देता है
- Dana कहती हैं, “JavaScript में भी recursion हो सकती है,” लेकिन उम्मीदवार जवाब देता है, “यह जल्द ही JavaScript नहीं रहेगा।”
- JavaScript की eager evaluation के कारण stack overflow हो जाता है, तो उम्मीदवार Skoobert नामक एक “lazy” JS interpreter में कोड चलाता है
- नतीजे में सफलतापूर्वक
3 आउटपुट होता है
arithmetic, lists, और strings
- उम्मीदवार SUBTRACT, MULTIPLY, MOD, DIVIDE जैसी सभी arithmetic operations को combinator-आधारित रूप में लागू करता है
- हर operation
IS_ZERO, PRED, SUCC आदि को recursively जोड़कर बनाई जाती है
- इसके बाद वह CONS, FIRST, REST, EMPTY को परिभाषित कर list structure बनाता है
MAP, FOLD, RANGE, CONCAT जैसी high-order functional operations भी combinator रूप में लागू की जाती हैं
- list को string के रूप में आउटपुट करने के लिए वह
renderList, showLines functions लिखता है
- Dana अब तक जैसे पूरी तरह हार मान चुकी हों, कोई प्रतिक्रिया नहीं देतीं
characters और strings की रचना
- उम्मीदवार character codes को 1 से 9 और फिर 10-10 की इकाइयों में combinators के जरिए परिभाषित करता है
CHAR_A से CHAR_Z और CHAR_0 से CHAR_9 तक सबको number combinations के रूप में व्यक्त किया जाता है
ARRAY function के जरिए strings को list में बदला जाता है, और FIZZ, BUZZ, FIZZBUZZ बनाए जाते हैं
extractString function के जरिए list को वास्तविक string में बदला जाता है
- console output का परिणाम
"fizzbuzz" होता है
numbers से strings में रूपांतरण
- वह
NUMBER_TO_DIGIT_LIST परिभाषित करता है, जो numbers को digit list में बदलता है, और NUMBER_TO_STRING, जो उसे string में बदलता है
DIVIDE, MOD, TEN आदि का उपयोग करके हर digit को अलग किया जाता है
DIGITS_NUMERAL list के जरिए number characters की mapping की जाती है
- इस प्रक्रिया से number → character → string रूपांतरण पूरी तरह combinator system के भीतर संभव हो जाता है
FizzBuzz की पूर्णता
FIFTEEN, ONE_HUNDRED को परिभाषित कर, MAP और RANGE की मदद से 1 से 100 तक की list बनाई जाती है
- हर संख्या के लिए 3, 5, 15 के multiples की जाँच कर
"Fizz", "Buzz", "FizzBuzz" या number string आउटपुट की जाती है
showLines(extractString)(FIZZBUZZ_RESULT) से अंतिम परिणाम आउटपुट होता है
- Dana पूछती हैं, “अब संतुष्ट हो?”, लेकिन उम्मीदवार सभी variable definitions हटा देता है और कोड को सिर्फ pure combinators से फिर से लिखता है
- परिणाम होता है हजारों लाइनों में फैला सिर्फ S और K से बना एक विशाल expression
अंत और अर्थ
- यह लेख प्रोग्रामिंग भाषा के मूलभूत घटकों की पड़ताल करने के साथ-साथ डेवलपर्स की उस प्रवृत्ति का व्यंग्य भी करता है, जिसमें वे साधारण समस्याओं को जरूरत से ज्यादा जटिल बना देते हैं
- “शून्य से भी कम के साथ प्रोग्रामिंग” शीर्षक भाषा की न्यूनतम इकाइयों से दुनिया को फिर से रचने की कोशिश का प्रतीक है
- यह रचना functional programming, lambda calculus, combinatory logic की गहरी समझ को हास्य और कथा के माध्यम से प्रस्तुत करने वाला तकनीकी साहित्य है
- साथ ही, यह व्यंग्यात्मक रूप से दिखाती है कि “FizzBuzz जैसी चीज़ को भी दार्शनिक प्रयोग में बदल देने वाली डेवलपर मानसिकता” कैसी होती है
1 टिप्पणियां
Hacker News राय
लगता है लोग सोच रहे हैं, “आखिर यह है क्या?”, इसलिए combinator का सार समझाता हूँ
combinator एक ऐसा function है जो global state को नहीं बदलता और बाहरी variables को capture नहीं करता। वही arguments देने पर यह हमेशा वही result देता है, और सिस्टम के बाकी हिस्सों पर कोई असर नहीं डालता
ऐसे pure functions को जोड़ने पर एक और combinator बनता है। यानी pure functions की composition भी pure function ही रहती है
ऐसे functions assembly या C में भी आसानी से लिखे जा सकते हैं। उदाहरण के लिए x64 पर S और K को सीधे implement करके, उसके ऊपर combinator-आधारित Common Lisp compile किया जाए, तो आखिरकार Lisp सिर्फ दो assembly functions के ऊपर चल रहा होगा
performance शायद बहुत अच्छी न हो, लेकिन अगर अक्सर इस्तेमाल होने वाले कुछ सौ patterns को inline combinator बनाकर नाम दे दिए जाएँ, तो यह काफ़ी practical “machine” बन सकती है
यह संरचना mutation से डरने वाले Forth, bytecode VM, या उन CPU जैसी भी लगती है जो combinator को “instruction” कहते हैं
आखिर में combinator को implementation details को जितना हो सके उतना नज़रअंदाज़ करके applied computer science की अभिव्यक्ति कहा जा सकता है। इसलिए इसे lambda calculus से भी सरल कहा जा सकता है
अगर lambda calculus को कुछ combinator से implement करके, उसके ऊपर Lisp रखा जाए, तो stack के सबसे निचले स्तर पर किए जाने वाले machine-dependent काम बहुत कम हो जाते हैं
इसे बस कुछ बार compose करना होता है
“lambda calculus वगैरह कभी इस्तेमाल मत करो। वह बहुत फूली हुई language है.”
लेकिन असल में combinatory logic उससे भी ज़्यादा फूली हुई है। वही program व्यक्त करने के लिए ज़्यादा bits चाहिए होते हैं
lambda calculus तो उल्टा सबसे संक्षिप्त languages में से एक है
JavaScript जैसी eager language में भी arguments को dummy lambda में लपेटकर उसे lazy बनाया जा सकता है। उदाहरण के लिए यह JS BLC interpreter देखें
संबंधित paper के लिए यह PDF और IOCCC उदाहरण देखें
“FizzBuzz जानते हो?”
“बिलकुल। 10-character code golf version, juniors के लिए समझ आने वाला 12-line version, या अजीब ज्ञान-प्रदर्शन वाला पूरी तरह उल्टा-पुल्टा version?”
नतीजे का लिंक
combinator logic की व्याख्या सच में शानदार थी। लिखने का अंदाज़ इस series की याद दिलाता है
मुझे पहले पढ़ा हुआ “FizzBuzz in TensorFlow” लेख याद आ गया
लिंक
इस तरह का programming interview frame थोड़ा ग़लत लगता है
interview का मकसद है (1) उम्मीदवार की programming knowledge जाँचना, (2) कंपनी की programming culture fit देखना
उदाहरण के लिए अगर JavaScript position के लिए भर्ती हो रही है और कोई combinatory logic में बहुत डूबा हुआ है, तो संभव है कि cultural fit न बैठे। इसलिए reject होना अजीब नहीं है
Moses Schönfinkel को याद करने का समय है
उन्होंने 1920 में Hilbert के छात्र रहते हुए combinatory logic का आविष्कार किया था। रूस लौटने के बाद वे मानसिक बीमारी से जूझे और ग़रीबी में मॉस्को में उनकी मृत्यु हुई। Wikipedia के अनुसार, उनके papers पड़ोसियों ने जलावन के लिए जला दिए थे
आख़िरी code block इतना बड़ा है कि horizontal scroll आ जाता है (166KB)
S और K curried functions हैं, जो एक बार में सिर्फ एक argument लेते हैं
ज़्यादा जानकारी के लिए यह StackOverflow answer देखें
“Bluebird, cardinal, warbler, thrush” जैसे पक्षियों वाले नाम बहुत पसंद आए। इन्हें नई naming convention बनाना चाहता हूँ
“Barendregt. Church तो बहुत mainstream है.”
“जल्द ही यह JavaScript भी नहीं रहेगा.”
ऐसा लगता है जैसे Douglas Adams, SICP पढ़ा रहे हों
“वह क्या… Y combinator है?”
“हाँ। recursion के लिए उसकी ज़रूरत होती है.”
मज़ेदार तथ्य: fixed-point combinator अनंत संख्या में हैं। यानी Y combinator के बिना भी recursion को implement करने के अनंत तरीके हैं