सी# में कार्यात्मक प्रोग्रामिंग: सिंटैक्स से आर्किटेक्चर तक
\n\nआधुनिक C# लंबे समय से विशुद्ध रूप से ऑब्जेक्ट-ओरिएंटेड भाषा की सीमाओं से बाहर आ चुका है। संस्करण 3.0 (LINQ) से शुरू होकर .NET के नवीनतम रिलीज़ तक, यह भाषा सक्रिय रूप से कार्यात्मक प्रोग्रामिंग (FP) के प्रतिमानों को आत्मसात कर रही है। यदि आपने लैम्ब्डा और LINQ में महारत हासिल कर ली है, लेकिन गहराई में जाना चाहते हैं, तो यह लेख आपके लिए है। हम FP की उन्नत अवधारणाओं पर चर्चा करेंगे जो आपके C# कोड को अधिक पूर्वानुमानित, परीक्षण योग्य और संक्षिप्त बनाएंगी।
\n\n1. शुद्ध फ़ंक्शन और साइड इफेक्ट्स के साथ काम करना
\n\nशुद्ध फ़ंक्शन वह फ़ंक्शन है जो:
\n- \n
- समान तर्कों के लिए हमेशा समान परिणाम लौटाता है (रेफरेंशियल ट्रांसपेरेंसी)। \n
- इसका कोई साइड इफेक्ट नहीं होता (ग्लोबल स्टेट को नहीं बदलता, फ़ाइल में नहीं लिखता, HTTP अनुरोध नहीं भेजता)। \n
C# में शुद्ध फ़ंक्शन का आदर्श उदाहरण गणितीय संक्रियाएँ हैं। समस्या यह है कि एक वास्तविक एप्लिकेशन केवल शुद्ध फ़ंक्शनों से नहीं बन सकता: देर-सबेर डेटा सहेजना या ईमेल भेजना आवश्यक हो जाता है। समाधान है लॉजिक को इफेक्ट्स से अलग करना।
\n\nउन्नत तकनीक: इफेक्ट्स को कंटेनराइज़ करने के लिए फ़ंक्टर और मोनाड का उपयोग करें। Maybe<T> (nullable का एनालॉग, लेकिन कार्यात्मक) के साथ एक सरल उदाहरण देखें:
public class Maybe<T>\n{\n private readonly T _value;\n public bool HasValue { get; }\n \n private Maybe(T value, bool hasValue) => (_value, HasValue) = (value, hasValue);\n \n public static Maybe<T> Some(T value) => new Maybe<T>(value, true);\n public static Maybe<T> None() => new Maybe<T>(default, false);\n \n // फ़ंक्टर: Select (Map)\n public Maybe<TResult> Select<TResult>(Func<T, TResult> selector) =>\n HasValue ? Maybe<TResult>.Some(selector(_value)) : Maybe<TResult>.None();\n \n // मोनाड: SelectMany (FlatMap/Bind)\n public Maybe<TResult> SelectMany<TResult>(Func<T, Maybe<TResult>> binder) =>\n HasValue ? binder(_value) : Maybe<TResult>.None();\n}\n\n// उपयोग\nMaybe<int> result = Maybe<int>.Some(10)\n .Select(x => x * 2)\n .SelectMany(x => x > 15 ? Maybe<int>.Some(x) : Maybe<int>.None());\n\nयह कोड कोई अपवाद उत्पन्न नहीं करता और मैन्युअल रूप से null की जाँच नहीं करता — मान की अनुपस्थिति को संभालने का सारा तर्क मोनाड में समाहित है।
2. करीइंग और आंशिक अनुप्रयोग
\n\nशास्त्रीय FP में, प्रत्येक फ़ंक्शन एक तर्क लेता है। करीइंग एकाधिक पैरामीटर वाले फ़ंक्शन को नेस्टेड फ़ंक्शनों की श्रृंखला में बदलना है, जिनमें से प्रत्येक एक तर्क लेता है। आंशिक अनुप्रयोग तर्कों के एक भाग को स्थिर करना है।
\n\nC# में अंतर्निहित करीइंग नहीं है, लेकिन इसे क्लोज़र के माध्यम से आसानी से लागू किया जा सकता है:
\n\n// सामान्य फ़ंक्शन\nFunc<int, int, int, int> add = (a, b, c) => a + b + c;\n\n// करीड संस्करण\nFunc<int, Func<int, Func<int, int>>> curriedAdd = a => b => c => a + b + c;\n\n// आंशिक अनुप्रयोग: पहला तर्क स्थिर करें\nFunc<int, Func<int, int>> add5 = curriedAdd(5);\n\n// उपयोग\nint sum = add5(10)(20); // 35\n\nउन्नत परिदृश्य: आंशिक अनुप्रयोग के माध्यम से डिपेंडेंसी इंजेक्शन. क्लास के कंस्ट्रक्टर के माध्यम से कॉन्फ़िगरेशन या सेवा पास करने के बजाय, उन्हें कंपोज़िशन के समय स्थिर किया जा सकता है:
\n\npublic class ReportService\n{\n public Func<string, string, string> FormatReport => \n (template, data) => $"[{DateTime.Now}] {template}: {data}";\n}\n\n// आंशिक अनुप्रयोग के साथ उपयोग\nvar service = new ReportService();\nvar formatWithTemplate = service.FormatReport("Error"); // टेम्पलेट स्थिर करें\nstring errorMessage = formatWithTemplate("Connection failed");