4 पॉइंट द्वारा GN⁺ 2023-08-20 | 2 टिप्पणियां | WhatsApp पर शेयर करें
  • Railway Oriented Programming एक ऐसा approach है जो validation, logging और network errors जैसे real-world applications में बार-बार आने वाले failure paths को functional pipeline के भीतर संभालता है
  • सिर्फ success path को जोड़ने वाले examples की सीमा को railway analogy से समझाना इसका मुख्य विचार है, ताकि F# users भी error flow को visually आसानी से समझ सकें
  • Haskell के नजरिए से यह Either type और Kleisli composition के करीब है, लेकिन abstract theory की बजाय error-handling recipes और implementation patterns पर focus करता है
  • Practical use के लिए NuGet के लिए F# library Chessie, C#·F# comparison GitHub examples, और FizzBuzz application example साथ में दिए गए हैं
  • यह उपयोगी तरीका है, लेकिन हर problem पर इसे जरूरत से ज्यादा लागू नहीं करना चाहिए; F# में type class नहीं है, इसलिए example library जरूरी functions को खुद define करती है

Presentation materials और example code

  • Railway Oriented Programming page presentation slides और code को एक जगह इकट्ठा करने वाला resource है
  • Functional programming examples अक्सर जिस “happy path” को assume करते हैं, सिर्फ उससे real-world applications बनाना मुश्किल है—यहीं से यह चर्चा शुरू होती है
  • Robust applications को validation, logging, network errors, service errors और अन्य exceptional situations को भी साथ में handle करना पड़ता है
    • लक्ष्य यह है कि ऐसी error handling को clean functional तरीके से कैसे संभालें, इसे railway analogy के जरिए छोटा और समझने में आसान बनाकर introduce किया जाए
    • मिलता-जुलता topic recipe for a functional app series में भी आगे जारी रहता है
    • Actual code C# और F# के ROP approaches की तुलना करने वाले GitHub project में देखा जा सकता है
    • Error handling के लिए उपयोगी होने के बावजूद इसे extreme तरीके से apply न करने की चेतावनी के साथ “Against Railway-Oriented Programming” भी reference के तौर पर दिया गया है

Video और slides

  • यह topic NDC London 2014 में present किया गया था
  • उसी presentation के दूसरे videos भी public हैं
  • Functional Programming eXchange में 14 मार्च 2014 की presentation slides उपलब्ध हैं
  • PowerPoint slides GitHub से भी डाउनलोड की जा सकती हैं, और बताया गया है कि उन्हें freely borrow करके use किया जा सकता है

Either monad और Kleisli composition

  • Haskell users को यह approach Either type जैसा लग सकता है
  • यहां इसे Left case में custom error types की list इस्तेमाल करने वाले रूप में specialize किया गया है
    • Example type है type TwoTrack a b = Either [a] (b,[a])
  • नया बनाया गया हिस्सा error handling concept खुद नहीं, बल्कि उसे समझाने वाली railway analogy के ज्यादा करीब है
  • Standard Haskell terminology को आगे न रखने की वजह साफ है
    • यह material monad tutorial नहीं है, बल्कि error-handling problem को solve करने पर केंद्रित है
    • F# से परिचित होने वाले कई लोग monads से familiar नहीं होते, इसलिए visual और कम डराने वाली explanation ज्यादा intuitive हो सकती है
    • bind वाले two-track type को सीधे monad कहना accurate नहीं है, और monad laws तक चर्चा नहीं जाती
    • Either बहुत general tool है, इसलिए यहां tool itself की बजाय recipe देने की कोशिश है

Error-handling recipe के components

  • यह approach “बस Either और bind इस्तेमाल कर लो” से ज्यादा comprehensive techniques का bundle है
  • इसमें शामिल techniques ये हैं
    • Either String a जैसे simple form के बजाय, left और right दोनों sides में custom error types की list का उपयोग
    • bind(>>=) से monadic functions को pipeline में integrate करना
    • Kleisli composition(>=>) से monadic functions compose करना
    • map(fmap) से non-monadic functions को pipeline में integrate करना
    • F# IO monad use नहीं करता, इसलिए tee से unit functions को pipeline में integrate करना
    • Exceptions को error cases में map करना
    • Validation जैसी situations में &&& से monadic functions को parallel combine करना
    • Domain-driven design में custom error types से मिलने वाले benefits का उपयोग
    • Logging, domain events और compensation transactions जैसे extensions
  • लक्ष्य एक ऐसा template बनाना है जो लगभग हर situation में use होने जितना versatile हो, फिर भी consistent style enforce करने जितना restrictive हो
  • जब code लिखने का तरीका practically एक ही दिशा में converge करता है, तो बाद में maintain करने वाला व्यक्ति code structure को जल्दी समझ सकता है
  • यह तरीका एकमात्र solution नहीं है, लेकिन अच्छी starting point हो सकता है
  • Haskell community के भीतर भी error handling approaches consistent नहीं हैं, इसलिए beginners के लिए यह confusing हो सकता है

अपने code में apply करने का तरीका

  • अगर तैयार F# library चाहिए, तो NuGet के साथ use की जा सकने वाली Chessie project देखी जा सकती है
  • इस technique को apply करने वाली sample web service GitHub project के रूप में दी गई है
  • ROP approach को FizzBuzz पर apply करने वाला example Railway Oriented Programming: carbonated में देखा जा सकता है
  • F# में type class नहीं है, इसलिए monads को reuse करने का कोई general तरीका नहीं है
  • FSharpX library useful approach देती है, लेकिन Rop.fs library सभी functions को शुरुआत से खुद define करती है
  • इस तरह isolate करने का फायदा यह है कि कोई external dependency बिल्कुल नहीं रहती

आगे पढ़ें

2 टिप्पणियां

 
GN⁺ 2023-08-20
Hacker News की राय
  • Elixir इस समस्या को with keyword/macro से काफ़ी साफ़ तरीके से संभालता है
    जैसे with {:ok, file_handle} <- File.open(filename), ... do {:ok, parsed} end में functions ऊपर से नीचे execute होते हैं, और अगर हर return value बाईं ओर के pattern से match नहीं करती, तो उसी value के साथ early return हो जाता है
    इसलिए हर function को {:ok, value} और {:error, reason} दोनों लेने के लिए बनाने की ज़रूरत नहीं पड़ती; function सिर्फ़ अपनी रुचि वाली value ले सकता है और with block की pattern matching को error propagation संभालने दिया जा सकता है
    उदाहरण के लिए, अगर File.open {:error, reason} return करता है, तो IO.read execute नहीं होगा और पूरे with का result {:error, reason} बन जाएगा. आखिर में आप सिर्फ़ success path लिखते हैं और failure path को caller चाहे तो match कर सकता है

    • अगर “जो match नहीं हुआ उसे early return करते हैं”, तो मैं सोचता हूँ कि जब वे values सब अलग-अलग type की हों तो क्या होगा. अगर dynamic typing है, तो क्या type देखकर यह अनुमान लगाना नहीं पड़ेगा कि failure कहाँ हुआ?
      यह exception handling को आधा फिर से invent करने जैसा लगता है, और catch की जगह else जैसा नाम दे दें तो बस यह exception handling जैसा कम दिखता है
    • मेरी जानकारी में यह Haskell के where और Lisp के let से प्रभावित रूप है
      यह function की preconditions set करने जैसा है, और Elixir में इसके अलावा else block भी इस्तेमाल कर सकते हैं
    • मैं सोचता हूँ कि यह exceptions से बेहतर कैसे है. और अगर IO.read fail हो जाए, तो file handle कौन close करेगा, यह भी समस्या है
  • लेखक ने कुछ साल बाद Against Railway Oriented Programming नाम से एक follow-up पोस्ट भी लिखी: https://fsharpforfunandprofit.com/posts/against-railway-orie...
    Railway Oriented Programming दिलचस्प है और इसके उपयोग की जगहें भी हैं, लेकिन मुझे लगता है कि इसके साथ बड़ी सावधानी वाली चेतावनी चाहिए। व्यवहार में मैंने इसे अक्सर exception handling को खराब तरीके से फिर से गढ़ने में इस्तेमाल होते देखा है, और exceptions को ठीक से समझकर इस्तेमाल किया जाए तो ज़्यादातर error conditions को ज़्यादा साफ़ और असरदार तरीके से संभाला जा सकता है
    exceptions का फायदा यह है कि ज़्यादातर मामलों में सुरक्षित विकल्प default होता है। error condition इस बात का संकेत है कि code specification के मुताबिक काम नहीं कर सकता, और उस स्थिति में आगे बढ़ते रहने से गलत assumptions के कारण data corrupt हो सकता है। exceptions default रूप से cleanup के बाद caller तक propagate होते हैं, लेकिन Railway Oriented Programming जैसी शैली में बहुत सा boilerplate चाहिए होता है जिसे भूलना और गलत करना आसान है
    हालांकि दो मामलों में यह उपयोगी है। एक, validation जैसी स्थिति जहां किसी specific और well-defined expected error को उसी जगह handle करना हो जहां वह पैदा हुआ है; दूसरा, ऐसा environment जहां exception handling इस्तेमाल नहीं की जा सकती। पुराने jQuery का Promise-based asynchronous code लगभग Railway Oriented Programming की implementation जैसा था, लेकिन आधुनिक JavaScript के async/await में फिर से exception handling इस्तेमाल की जा सकती है

    • mainstream languages के exceptions में कमी यह लगती है कि यह handling अदृश्य रूप से होती है। compile time पर यह पता लगाना आसान नहीं होता कि किसी function के अंदर से कौन से exceptions आ सकते हैं
      Railway Oriented Programming का बड़ा फायदा यह है कि function signature देखकर ही तुरंत पता चल जाता है कि कौन-सी errors आ सकती हैं
      boilerplate को भाषा के हिसाब से कम किया जा सकता है। Haskell में do notation है, और F# में result computation expression से इसे बहुत साफ़ तरीके से लिखा जा सकता है। उदाहरण के लिए LoginError = InvalidUser | InvalidPwd | Unauthorized of AuthError की तरह define करके, Result.requireSome, Result.requireTrue, Result.mapError से flow बनाया जा सकता है
      इसके उलट, मैं जानना चाहता हूं कि क्या exceptions की कमियां भी कम की जा सकती हैं। क्या Java IDE के साथ integrate होने वाला कोई linter या static analysis tool है जो किसी specific code line से throw हो सकने वाले uncaught exceptions को अपने-आप दिखा दे
      https://demystifyfp.gitbook.io/fstoolkit-errorhandling/fstoo...
    • मुझे पारंपरिक exception implementation का तरीका काफ़ी खराब लगता है
      सबसे पहले, कई languages exceptions पकड़ने के लिए अटपटा और अनावश्यक scope मजबूर करती हैं। जैसे, exception throw कर सकने वाले function के result से variable declare/initialize करना हो, और failure पर कोई दूसरा value डालना हो, तो declaration और initialization अलग करने पड़ते हैं
      बड़ा मुद्दा यह है कि error conditions को उस सबसे निचले level पर handle करना चाहिए जहां उन्हें सही तरीके से handle करने के लिए पर्याप्त context हो, जबकि exceptions का default “सबसे ऊपर तक throw करना” है। जैसे, सिर्फ़ एक file cache जोड़ने से ऊपर का HandleRequest function अचानक I/O exception throw करने में सक्षम हो जाता है, और abstraction leak होना आसान है
      मेरा मानना है कि function जो कुछ भी return कर सकता है, वह सब explicitly signature का हिस्सा होना चाहिए, और caller को या तो explicitly handle करना चाहिए या ऊपर pass करने का संकेत देना चाहिए। language को इसके लिए बहुत boilerplate मांगने की ज़रूरत नहीं है
    • मुझे लगता है Rust ने अच्छी तरह दिखाया है कि first-class syntax support वाली Railway Oriented Programming boilerplate को लगभग खत्म कर सकती है। व्यक्तिगत रूप से, Rust के Result type/trait ने error handling को सही तरह से कर दिखाया है
    • value के रूप में errors और exceptions के बीच फर्क domain-specific errors समझाते समय सबसे उपयोगी लगता है। domain errors को value के रूप में व्यक्त करना सही है, और database connect न हो पाने जैसी domain से unrelated problems के लिए exception ज़्यादा natural है
      file या record न होने पर भी यह expected है या नहीं, इस पर निर्भर करता है। अगर user द्वारा दिए गए ID से lookup करने वाली request है, तो वह lookup खुद user input validation है। लेकिन अगर database record में stored blob name से storage lookup किया और blob नहीं मिला, तो यह exception के रूप में handle करने लायक स्थिति है
      errors और exceptions की सीमा धुंधली है और team व problem domain के अनुसार बदलती है, लेकिन मुझे यह ठीक-ठाक rule of thumb लगता है
    • system-level errors को छोड़ दें तो, मैं ऐसी error state का उदाहरण जानना चाहता हूं जो “उत्पत्ति-स्थल पर handle की जानी वाली concrete और well-defined expected error” न हो। मैं समझना चाहता हूं कि ऐसे मामलों में exception बेहतर होने की वजह problem की nature है या runtime constraints
      C++ code में शुरुआत में data की जांच करना, जांच fail होने पर application को crash कर देना, और मिले हुए error state के आधार पर शुरुआती जांच code को debug/fix करना जैसे तरीके से handle किया जा सकता है
      जिस data से काम है वह ज्ञात CAD data format या geometric topology के अनुसार अपेक्षाकृत स्पष्ट data है, इसलिए पहले यह समझ पाना कि valid data क्या है, error conditions संभालने को “काफी आसान” बनाता है
  • सच कहूं तो यह पूरा site ही खज़ाना है। functional language इस्तेमाल करने का इरादा न हो तब भी यह एक अलग नज़रिया देता है, और व्यक्तिगत रूप से इससे मुझे बहुत मदद मिली। बाकी posts भी recommend करता हूं

    • शुरुआत से parser combinator बनाने वाली series मेरे द्वारा पढ़कर follow की गई चीज़ों में सबसे valuable resources में से थी। उसमें आए कई concepts और mechanisms typed languages को functional style में संभालते समय बेहद उपयोगी रहे
      फिर भी मुझे लगता है कि monad क्या है, यह मैं आखिर तक नहीं समझ पाऊंगा
      https://fsharpforfunandprofit.com/series/understanding-parse...
  • मुझे जानना है कि “error handling को call site से दूर धकेलना खराब है” वाला दावा सही बैठता है या नहीं। मुझे लगता है कि caller ही error को सबसे अच्छे तरीके से handle कर सकता है, तो उसे नीचे पास करने के बजाय सीधे handle करना चाहिए
    उदाहरण के लिए validate and-then update-db and-then send-email flow में validate failure को caller को लौटाना ठीक हो सकता है। लेकिन अगर update-db fail हो जाए, तो तय करना होगा कि retry करना है, कोई दूसरी service इस्तेमाल करनी है, queue में फिर डालना है, या user को बताना है। send-email failure भी ऐसा ही है
    आखिर में लगता है कि structure कहीं ज़्यादा complex branching वाला हो जाएगा: ValidationError पर user को notify करना, DBError पर 5 मिनट बाद retry करना या remote retry के बाद notify करना, और EmailFailed पर email को फिर से queue में डालकर success मान लेना। शायद यह भी ठीक हो सकता है

    • monadic error handling में दोनों तरफ़ के फायदे मिलते हैं
      Go को नापसंद करने वाले लोग शिकायत करते हैं कि error conditions बहुत बार check करनी पड़ती हैं, और Java के checked exceptions भी कुछ ऐसे ही हैं। Either इस्तेमाल करने पर check करने के लिए मजबूर नहीं किया जाता; आप खुद check कर सकते हैं या caller को आगे पास कर सकते हैं
      Java के unchecked exceptions को नापसंद करने वाले लोग शिकायत करते हैं कि पता नहीं चलता क्या कब throw होगा। Either इस्तेमाल करने पर यह explicit हो जाता है
      असली बात यह है कि error होने पर क्या करना है, यह हमें नहीं पता होता, और Either इस अनिश्चितता के लिए अच्छी तरह fit बैठता है। मौजूदा Java codebase में Limit getUserLimit(User) न मिलने पर NotFound exception throw कर सकता है, null return कर सकता है, या default value return कर सकता है; code में गहराई से देखे बिना यह पता नहीं चलता
      अगर Either होता, तो caller internal code पढ़े बिना भरोसा कर सकता था, और orElse(null) या orElseThrow(...) जैसे built-in tools से उसे आसानी से किसी दूसरी failure condition में बदल सकता था। साथ ही Either एक expression है, इसलिए abstraction के लिए अच्छा है; DbUpdater के अंदर specialized retry code रखने के बजाय general retry logic लिखा जा सकता है
    • मैंने इस logic को Java के try-catch style में implement करके देखा, लेकिन nested try, catch(DBError), sleep(5), remote retry, EmailFailedError requeueing, और ValidationError notification आपस में उलझकर कहीं ज़्यादा खराब दिखते हैं
      https://gist.github.com/Andrewp2/9d97bd213b061166d6df565ce26...
      कोई शायद बेहतर try-catch version लिख सकता है, लेकिन मुझे लगता है कि यह दिए गए railway-style version से काफी ज्यादा खराब है
    • low-level और high-level error handling साथ-साथ रह सकते हैं, लेकिन मुझे लगता है कि यह style blackboard examples या एक बार बनाकर छोड़ देने वाली situations में ज़्यादा fit बैठता है
      DDD मुझे पसंद है, लेकिन मेरे जैसे साधारण programmer के लिए maintainable और reasonable maintenance cycle चाहिए। mutable code में TDD अच्छा इसलिए था क्योंकि उसने maintenance की cognitive load घटाई थी
      railway-style programming कई transformations से गुजरते हुए काफी context ढो सकती है, और track के end पर ऐसी train पाने की स्थिति से बचना चाहूंगा जिसमें बहुत सारा business और system context लदा हो जिसके लिए वह तैयार नहीं है
      मुझे लगता है DDD और onion architecture दोनों तरफ़ के फायदे देते हैं। आखिरकार railway बनती है, लेकिन वह micro app के अंदर धंसी हुई नहीं होती, बल्कि बाहर होती है। railway-style issues को explicit और थोड़ा बोझिल तरीके से code करना बेहतर है, और अगर काम अच्छा किया गया हो तो ज़्यादातर railway-style decisions ऐसी business decisions होती हैं जिन्हें clever coding structure से avoid नहीं करना चाहिए
      monadic programming बेहतरीन है, लेकिन इसे जरूरी business decisions से बचने के लिए इस्तेमाल करने का temptation होता है। ambiguity को type system में डालने से आगे चलकर मुश्किल या असंभव हालात बन सकते हैं
    • low-level और high-level error handling साथ-साथ रह सकते हैं। यह किसी बड़े try/catch के अंदर specialized try/catch होने से अलग नहीं है
      उदाहरण के लिए send-email function के अंदर आप बहुत तेज़ transient errors को handle करना चुन सकते हैं। अगर allowed time के भीतर connection हो जाए तो success path पर वापस आ जाए, नहीं तो caller को return कर दे
      अगर कोई पक्का rule है, तो वह यह है कि जिस handling में human intervention चाहिए, उसे जरूर wrap करके ऊपर पास करना चाहिए। success path पर वापस आ जाए तब भी warning log छोड़ना बेहतर है
    • आपके दिए example की तरह end हो सकता है, लेकिन वही basic top-level pipeline रखते हुए इसे अलग तरह से compose किया जा सकता है
      notify-user ValidationError और DBError के हिसाब से user notifications संभाले, try-update-db update-db, 5 मिनट बाद retry, और remote retry को bundle करे, और try-send-email failure होने पर email को फिर से queue में डालकर उसे success में बदल दे
      तब top-level validate and-then try-update-db and-then try-send-email or-then notify-user जैसा बना रहेगा। लगता है and/or/then को map/bind के हिसाब से ठीक से translate किया गया है
  • यह site practical और उपयोगी concepts सिखाने के मामले में मेरे देखे हुए programming education sites में सबसे अच्छी थी। खासकर गलत states को represent करना असंभव बनाना वाला concept मुझे पसंद है, और मैं इसे भाषा की परवाह किए बिना apply करने की कोशिश करता हूं। बेशक कुछ भाषाएं इसे आसान बना देती हैं
    https://fsharpforfunandprofit.com/posts/designing-with-types...

  • call/return के बजाय डेटा flow इस्तेमाल करने पर यह समस्या बेहतर ढंग से हल होती है, और काफी हद तक अपने-आप गायब हो जाती है
    call/return में कुछ न कुछ return करना पड़ता है, इसलिए अगर error की वजह से return करने के लिए कोई value न हो तो error return करना पड़ता है, या Go की तरह error और normal return value दोनों साथ में return करने पड़ते हैं। इससे success path दूषित हो जाता है। polymorphic container से बाकी processing को पिरोकर आगे बढ़ाने पर थोड़ा सुधार होता है, लेकिन समस्या बनी रहती है
    डेटा flow में अगले filter step को कुछ भी न भेजना काफी है, इसलिए success path पर बिल्कुल असर नहीं पड़ता। error को standard error output जैसी जगह भेजा जा सकता है, और application level पर centralize किया जा सकता है
    यह बहुत अच्छा लग सकता है, लेकिन Wunderlist में इसे सचमुच इस्तेमाल किया गया था और यह अच्छी तरह काम करता था। जब value न हो तब इस्तेमाल की जाने वाली वही technique उस समय भी ज्यों-की-त्यों लागू होती है जब value अभी न हो, यानी asynchronous processing में

    • मुझे जिज्ञासा है कि क्या यह data return करने या exception throw करके कहीं और catch होने देने से मूल रूप से अलग है
    • समझने में मदद के लिए code example हो तो अच्छा होगा
    • उस तरीके की भी कमियां हैं, और खासकर मुझे यह सीमित लगता है। exception handling, खासकर asynchronous exception handling, error recovery को कठिन बना सकती है
      किसी simple data stream में error आए तो अगले step के लिए data नहीं है, इसलिए रुक जाना ठीक है। लेकिन अगर sqrt(-1) पर तुरंत “crash” करके error output या logging पर कूद जाए, तो कुछ खास use cases में negative numbers के लिए कोई अलग value देने हेतु domain को फैलाना चाहें तो मुश्किल हो जाती है
      ऐसे में Maybe जैसा explicit error type उपयोगी होता है। success path को ऐसे लिखें जैसे कोई error नहीं है, लेकिन function composition के लिए अलग operator इस्तेमाल करें। अगर error से recover करना हो तो उसी जगह दोनों paths को handle करें, और उसके बाद error की अनुमति न भी दें। abstract “हो भी सकने वाली value” से actual value निकालने के लिए दोनों paths को सलीके से handle करना पड़ता है—यह बात खास तौर पर अच्छी है
      explicit error type इस्तेमाल करने पर success/error की द्विआधारी सोच से भी बाहर निकला जा सकता है। “value नहीं है, लेकिन”, “value है, लेकिन” जैसी states भी व्यक्त की जा सकती हैं, और parallel में process किए जा सकने वाले कई tasks में errors को accumulate करके result के रूप में जुटाया जा सकता है। इससे आगे, nondeterminism को encode करके graph traversal जैसी चीजों में भी इस्तेमाल किया जा सकता है
      Haskell programmers की तरह state को pure तरीके से carry भी किया जा सकता है, और कुख्यात Haskell का IO monad असल path को व्यक्त करने से ज्यादा बाहरी दुनिया से संपर्क के points को main तक सीमित करने वाला mechanism है। यह purity के लिए है, लेकिन मेरे हिसाब से purity हमेशा absolute रूप से पीछा किए जाने लायक value नहीं है
      यह technique simple railway से कहीं ज्यादा विविध है, और purity बनाए रखते हुए सिर्फ success path लिखने का साफ-सुथरा तरीका देती है। equation-based static analysis और mock-less unit tests के लिए भी फायदेमंद है। काश और mainstream languages ऐसा monad toolbox बेहतर ढंग से उपलब्ध कराएं
  • संबंधित links ये हैं: Railway Oriented Programming - https://news.ycombinator.com/item?id=31404643 - मई 2022, Railway-Oriented Programming (2015) - https://news.ycombinator.com/item?id=17337155 - जून 2018, Railway oriented programming - https://news.ycombinator.com/item?id=11955917 - जून 2016, Railway Oriented Programming - https://news.ycombinator.com/item?id=9166943 - मार्च 2015, Railway-oriented programming - https://news.ycombinator.com/item?id=7887134 - जून 2014
    इसके अलावा What is railway oriented programming? (2020) - https://news.ycombinator.com/item?id=34245639 - जनवरी 2023, Railway Oriented Programming in Elixir (2015) - https://news.ycombinator.com/item?id=11958578 - जून 2016

  • यह style दूसरे monads की तरह संक्रामक है, इसलिए सारा business logic इसी आकार का हो जाता है। अगर यह आपको ठीक लगे तो आगे पढ़ सकते हैं
    exceptions, नाम के मुताबिक, unexpected errors हैं, और जहां catch किया जाता है वहां तक stack को unwind करते हुए जरूरी cleanup अपने-आप हो जाता है। ये I/O error या out of memory जैसी उन errors के लिए उपयुक्त हैं जो business function के scope के बाहर पैदा होती हैं
    अगर data validate करना हो तो exception handler या monad पर निर्भर होने की जरूरत नहीं। functional pipeline के result को data बना दें। validation errors हों तो उन्हें set के रूप में इकट्ठा करें, और pipeline खत्म होने के बाद check करें। आम तौर पर पहली error पर fail करना शायद आप नहीं चाहेंगे
    अगर pipeline में side effect चाहिए, तो ऐसा न करें; उस effect को data के रूप में describe करें और बाद में execute करें। बीच में जल्दी बाहर निकलना हो तो उसी point पर pipeline को split करें, result को process करें और फिर किसी दूसरी pipeline में डाल दें
    या interceptor chain इस्तेमाल करके यह तय करवाया जा सकता है कि success path जारी रखना है या नहीं। language support करे तो pattern matching इस्तेमाल करें। अहम बात यह है कि pipeline के किसी हिस्से को skip करना है या नहीं, इसका फैसला business function के बाहर होता है; तब business function सिर्फ pure data transformation पर ध्यान देता है, जिससे उसकी reusability बढ़ती है

  • ऐसे विषय का फिर से सामने आकर चर्चा में आना ही अच्छी बात है। मैंने इस साइट को 1 साल तक पढ़ा और उदाहरणों को follow किया, और यह सबसे अच्छे F# resources में से एक था
    इस साइट में बताए गए concepts भर से ही किसी भी language में functional सोच रखने में मदद मिली
    यह पुरानी साइट है, लेकिन अगर अब फिर ध्यान पा रही है तो सोचता हूँ कि क्या इसका मतलब है कि F# momentum पकड़ रहा है। और यह भी जानना चाहता हूँ कि क्या दूसरी languages के programmers भी Rust की तरह railway-style error handling इस्तेमाल करते हैं

    • मेरा background desktop applications के लिए C# में काफ़ी मजबूत है, और समय के साथ मेरा C# coding style ज्यादा functional होता गया है। जैसे Linq का बहुत इस्तेमाल करना, immutable classes और detailed error information रख सकने वाले return types इस्तेमाल करना
      फिर भी F# पर switch न कर पाने की वजह यह है कि मेरा ज़्यादातर समय desktop frontend को adjust करने में जाता है। इस क्षेत्र में F# कोई खास फायदा देता हुआ नहीं लगता। Production-ready desktop frontend native F# नहीं, बल्कि ज़्यादातर C#-based object-oriented code होता है, और F# से बस पतले glue code के जरिए जुड़ता है
      जैसे ही थोड़ा भी complex UI customization चाहिए होता है, फिर object-oriented दुनिया में लौटना पड़ता है, और ऐसा है तो मेरे हिसाब से सीधे C# में करना बेहतर है। निजी तौर पर, F# को momentum पकड़ना है तो उसे native UI framework चाहिए
    • C++ में भी ऐसा तरीका इस्तेमाल करता हूँ। मैं एक template return type define करता हूँ जिसे return type T और result condition define करने वाले किसी specific enum से parameterize किया जा सके
      जल्दी में हो तो std::pair से भी कर सकते हैं, और error message को string में रखकर return करना, फिर string खाली न हो तो early exit करना भी संभव है। अगर हर function ऐसा type return करे, तो error message को top level तक bubble up किया जा सकता है
  • मुझे अच्छा लगता है कि यह लेख हर कुछ साल में फिर सामने आ जाता है। यह सचमुच बेहतरीन लेख और presentation है
    हर बार दोबारा पढ़ते समय मैं अपनी technical journey में skill या philosophy के लिहाज से अलग जगह पर होता हूँ, इसलिए हर बार यह अलग दिखता है—यह भी दिलचस्प है

    • ऐसे resources सच में पसंद हैं। Python talks में “so you want to be a python expert” नाम की talk मैंने शुरुआती कुछ सालों तक हर 6 महीने में देखी थी, और हर बार उसे ज्यादा समझ पाया। अब फिर देखूँ तो शायद कुछ नया मिल ही जाए