बाएँ से दाएँ प्रोग्रामिंग करना
(graic.net)- बाएँ से दाएँ प्रोग्रामिंग करने का तरीका यह सुनिश्चित करता है कि कोड टाइप करते ही प्रोग्राम वैध स्थिति में बना रहे, जिससे editor autocomplete जैसे tool support का अधिकतम लाभ मिलता है
- Python के list comprehension में घोषित न किए गए variables और type inference की अनुपस्थिति autocomplete को बाधित करती है
- Rust और JavaScript में प्रोग्राम को स्वाभाविक रूप से बाएँ से दाएँ बनाया जा सकता है, जिससे variable usage और method discovery अधिक सहज हो जाती है
- C और Python की functional style में function names या structure की discoverability की कमी efficient coding experience को कमजोर करती है
- उच्च जटिलता वाले logic में बाएँ से दाएँ unfold होने वाला code अधिक पढ़ने योग्य होता है, और maintenance व extensibility में बेहतर होता है
बाएँ से दाएँ प्रोग्रामिंग करना
कोड टाइप करते ही वैध होना चाहिए
Python list comprehension की सीमाएँ
- Python का list comprehension syntax
words_on_lines = [line.split() for line in text.splitlines()]ऐसी समस्या पैदा करता है जिसमें undeclared variable (line) तक पहुँचना पड़ता है, इसलिए editor autocomplete या type inference ठीक से उपलब्ध नहीं करा पाता - कोड को आंशिक रूप से टाइप करने की प्रक्रिया में
- अगर
words_on_lines = [line.spतक टाइप किया जाए, तो editorlineका type नहीं जानता, इसलिए methods सुझा नहीं सकता - variable name की typo (
limeआदि) जैसी संभावित errors को पकड़ना भी कठिन हो जाता है
- अगर
- सही suggestions पाने के लिए incomplete code लिखना पड़ता है, और यह प्रक्रिया गैर-सहज और असुविधाजनक हो जाती है
Rust में बाएँ से दाएँ निर्माण
- Rust का उदाहरण
let words_on_lines = text.lines().map(|line| line.split_whitespace());यह दिखाता है कि- anonymous function की declaration के साथ variable (
line) पहली बार आते ही declared माना जाता है, इसलिए तुरंत autocomplete और method suggestions संभव हो जाते हैं - वास्तव में
split_whitespacemethod भी autocomplete की मदद से आसानी से मिल गया
- anonymous function की declaration के साथ variable (
- यह तरीका प्रोग्राम को हमेशा कम से कम आंशिक रूप से वैध स्थिति में रखता है, इसलिए IDE या editor real time में coding support दे सकते हैं
Progressive Disclosure और API usability
- Progressive Disclosure एक design principle है जिसमें user केवल उतनी ही complexity देखता है जितनी उसे चाहिए, और इसे programming पर भी लागू किया जा सकता है
- उदाहरण: word processor में image जोड़ने पर ही संबंधित options दिखना, यह उसी तरह का UX है
- C language में ऐसा support कमज़ोर है
FILE *fileसे जुड़े functions कोfile.के ज़रिए explore नहीं किया जा सकता, इसलिएfread,fcloseजैसे function name patterns याद रखने पड़ते हैं और capabilities खोज पाना कठिन होता है- इसके विपरीत, किसी आदर्श language में
file.के माध्यम से method suggestions के जरिए संबंधित functionality को आसानी से क्रमिक रूप से खोजा जा सकता है
Functions और methods की discoverability का अंतर
- Python के
map(len, text.split())और JavaScript केtext.split(" ").map(word => word.length)उदाहरणों की तुलना- Python में
len,length,sizeजैसे function names का अनुमान लगाना आसान नहीं होता, इसलिए कई बार कोशिश करनी पड़ती है कि वास्तव में क्या काम करेगा - JavaScript में
word.के बाद सिर्फ.lटाइप करने पर भी editorlengthजैसी methods सुझा देता है, इसलिए discoverability अधिक होती है mapजैसे higher-order functions में भी actual return value और data type तुरंत अधिक स्पष्ट दिखाई देते हैं
- Python में
Logic जितना जटिल, structured writing के फायदे उतने अधिक
- अधिक जटिल logic (जैसे
filter,lambdaवाले nested लंबे Python code) में- code की शुरुआत और अंत बार-बार देखना पड़ता है, और condition expressions या parentheses matching जैसी चीज़ों में readability घटती है और समझना कठिन हो जाता है
- उसी logic के JavaScript version में code को ऊपर से नीचे, बाएँ से दाएँ क्रमवार पढ़ा और समझा जा सकता है
मुख्य सिद्धांत
कोड टाइप करने के हर क्षण वैध होना चाहिए
- सिर्फ
textटाइप करने पर भी प्रोग्राम वैध स्थिति में रहता है text.split(" ")तक लिखने पर भी, और फिर आगे.map(word => word.length)जोड़ते समय भी, पूरा code हमेशा intermediate state में वैध रहता है- ऐसा coding pattern editor के real-time support की संभावना बढ़ाता है, और REPL environment में तुरंत परिणाम देखना भी संभव बनाता है
निष्कर्ष
- API और language design को ऐसा समर्थन देना चाहिए कि code को बाएँ से दाएँ स्वाभाविक रूप से टाइप करते हुए हर intermediate stage पर वैध प्रोग्राम बनाया जा सके
- अच्छा API design इस coding experience को बेहतर बनाने की कुंजी है
1 टिप्पणियां
Hacker News राय
FROMकी बजायSELECTसे शुरू होती है, इसलिए यह तुरंत समझना मुश्किल होता है कि किस entity (table) पर काम हो रहा है, और smart editor के लिए query लिखने में ज़्यादा प्रभावी मदद करना भी कठिन हो जाता है,FROM -> SELECT -> WHEREक्रम ज़्यादा स्वाभाविक लगता है, खासकर क्योंकिSELECTclause में column names तय किए जाते हैं औरWHEREमें उन्हें refer किया जाता है, इसलिए और भी ऐसा लगता है, वास्तव मेंSELECT * FROM tableकी जगह सिर्फFROM tableलिखने पर भीSELECTclause छोड़ा जा सकता है, मुझे पता है ऐसी शिकायत से मैं किसी बड़बोले बूढ़े की तरह लग सकता हूँ, लेकिन यह बस मेरी निजी कसक हैFROM-first query order का इस्तेमाल करते हैं, BigQuery में भी हाल ही में pipe/arrow syntax जोड़ा गया है, और DuckDB community extension भी हैं, इसलिए इन्हें recommend करता हूँ DuckDB - PSQL, DuckDB - PRQLWHEREमें column alias नहीं इस्तेमाल किए जा सकते, क्योंकि selection(WHERE) projection(SELECT) से पहले होती है, वैसे MySQL 8 मेंTABLE <table>नाम का syntax भी है, देखना दिलचस्प हो सकता हैFROM -> WHERE -> SELECTहोता है, इसलिएSELECTमें define किए गए column aliasGROUP BY,HAVING,ORDER BYमें तो इस्तेमाल होते हैं, लेकिनWHEREमें नहींFROM-first structure रखता है, और IDE में दूसरे clauses लिखते समय autocomplete की वजह से field suggestions तुरंत मिल जाती हैं, इसलिए यह structure मुझे अच्छा लगता हैFROMसे शुरू होने वाले variants को थोड़ा और सक्रिय रूप से लाया जाना चाहिए, और मुझे नहीं लगता कि यह कोई मुश्किल काम है, usability सुधारने की कोशिश कम दिखती हैforloop जैसी ही हैं, सब लोग Python के loose typing को लेकर चिंतित रहते हैं, लेकिन return type को साफ़ करने वाला एकमात्र syntax(list comprehension) ही निशाने पर आता है, यह अजीब है, Rust समेत ज़्यादातर दूसरी भाषाओं में भी क्रमfrom iter as varनहीं है, और अलग-अलग भाषाओं ke function call syntax की तुलना करना भी मज़ेदार है(Python में भीfunctools.mapहै)returnआदि){3} for {2} in {1}जैसा क्रम भी संभव है, ऐसे tools “पढ़ने में अच्छी syntax” और “टाइप करने में आसान syntax” के बीच समझौता देते हैं, मैं tooling का सहारा लेकर भी readability को प्राथमिकता देने के पक्ष में हूँ, मुझे नहीं लगता कि सिर्फfor-instructure पर अड़े रहना चाहिएlet foo = ...की तरह result को variable में assign करने के बजाय... =: fooजैसा form भी आज़माना चाहूँगाbake(divide(add(knead(mix(flour, water, sugar, butter)),eggs),12),450,12)की तरह nested लिखने के बजाय,mix(flour, water, sugar, butter) %>% knead() %>% add(eggs) %>% divide(12) %>% bake(temp=450, minutes=12)की तरह pipe के साथ लिखना बहुत आसान और साफ़ दिखता हैI drink water→Ich trinke Wasser), यह हमेशा पूरी तरह sentence-end पर नहीं होताfrom some_library import child_modulesyntax बहुत intuitive है, JS मेंimport { asYetUnknownModule } from SomeLibraryजैसी structure है, इसलिए वह काफ़ी कम intuitive लगती हैfromkeyword पर इतने अटके क्यों रहते हैं, बस ऐसा भी तो किया जा सकता हैlen(list(filter(lambda line: all([abs(x) >= 1 and abs(x) <= 3 for x in line]) and (all([x > 0 for x in line]) or all([x < 0 for x in line])), diffs)))की तरह लिखने के बजाय NumPy array का उपयोग करना बेहतर है, इससे memory में नई list बनाने की ज़रूरत नहीं पड़ती और पूरी line को एक बार में handle करना भी आसान होता है, उदाहरण के लिए यह रूप कहीं ज़्यादा “left to right” महसूस होता हैline > 0तो ठीक है, लेकिन broadcasting rules जटिल हो सकते हैं), लेखक के JavaScript example या C#, Java, Scala जैसी type-strict भाषाओं की collection APIs ज़्यादा साफ़ लगती हैं, मेरी पसंद Kotlin है, क्योंकि वहाँ इसे ऐसे लिखा जा सकता है