Kotlin DSL: अपनी खुद की प्रोग्रामिंग भाषा बनाएं एक शाम में

शुरुआती लोगों के लिए ऑनलाइन Python प्रशिक्षक

Python आसानी से सीखें बिना सिद्धांत के बोझ के। स्वचालित जांच के साथ व्यावहारिक कार्य हल करें और ब्राउज़र में सीधे कोड लिखें।

पाठ्यक्रम शुरू करें

DSL क्या है और आपको इसकी आवश्यकता क्यों है?

\n\n

कल्पना करें कि आप कोड नहीं, बल्कि एक सरल भाषा में निर्देश लिख रहे हैं, जो खुद ही एक काम करने वाले प्रोग्राम में बदल जाता है। ठीक इसी तरह डोमेन-विशिष्ट भाषाएं (DSL - Domain Specific Language) काम करती हैं। सामान्य भाषाओं (Java, Python, C#) के विपरीत, DSL किसी एक विशिष्ट कार्य को हल करने के लिए बनाई जाती है - चाहे वह सर्वर कॉन्फ़िगरेशन हो, बिज़नेस नियमों का वर्णन हो, या जटिल SQL क्वेरी बनाना हो।

\n\n

Kotlin DSL बनाने के लिए एक आदर्श भाषा है। रिसीवर के साथ लैम्ब्डा, एक्सटेंशन और type-safe builders की बदौलत, आप ऐसा कोड लिख सकते हैं जो मूल सिंटैक्स जैसा दिखता है। इस लेख में हम सिर्फ थ्योरी नहीं, बल्कि एक वास्तविक DSL बनाएंगे - और आप देखेंगे कि यह कितना आसान और मजेदार है।

\n\n

Kotlin DSL का राजा क्यों है?

\n\n

Kotlin ने डेवलपर्स का दिल सिर्फ Java के साथ संगतता के कारण नहीं जीता। इसकी सिंटैक्टिक क्षमताएं आपको आंतरिक DSL बनाने की अनुमति देती हैं - ऐसी भाषाएं जो होस्ट भाषा के अंदर रहती हैं और उसके सिंटैक्स का उपयोग करती हैं। ये हैं तीन मुख्य स्तंभ जिन पर कोई भी Kotlin DSL टिका होता है:

\n\n
    \n
  • रिसीवर के साथ लैम्ब्डा - बिना नाम बताए ऑब्जेक्ट के मेथड्स को कॉल करने की अनुमति देता है। यह builder-पैटर्न का आधार है।
  • \n
  • एक्सटेंशन - किसी भी क्लास में नए फंक्शन जोड़ते हैं, भले ही आप उसे बदल नहीं सकते।
  • \n
  • इनफिक्स फंक्शन - प्राकृतिक भाषा की शैली में कोड लिखने की अनुमति देते हैं: should be equalTo 5।
  • \n
\n\n

ये क्षमताएं DSL लिखने को Lego कंस्ट्रक्टर में बदल देती हैं: आप तैयार ब्लॉक - लैम्ब्डा, फंक्शन, प्रॉपर्टी - लेते हैं और उनसे एक स्पष्ट और सुरक्षित भाषा बनाते हैं।

\n\n

टेस्ट के लिए एक सरल DSL बनाएं: कदम दर कदम

\n\n

चलिए टेस्ट लिखने के लिए एक DSL बनाते हैं। लक्ष्य यह है कि टेस्ट एक पठनीय विवरण की तरह दिखें, न कि assert कॉल के सेट की तरह:

\n\n
test("कैलकुलेटर को संख्याओं को जोड़ना चाहिए") {\n    val result = Calculator.add(2, 3)\n    result should beEqualTo 5\n    result shouldNot beNull\n}
\n\n

मान गए, यह लगभग हिंदी जैसा लगता है। अब समझते हैं यह कैसे काम करता है।

\n\n

चरण 1. टेस्ट कॉन्टेक्स्ट बनाएं

\n\n

हमें एक test फंक्शन चाहिए जो नाम और कोड का ब्लॉक ले। ब्लॉक एक ऐसे कॉन्टेक्स्ट में चले जहां should और shouldNot उपलब्ध हों:

\n\n
class TestContext {\n    fun Any.should(matcher: Matcher) {\n        if (!matcher.matches(this)) {\n            throw AssertionError("उम्मीद थी ${matcher.expected}, लेकिन मिला $this")\n        }\n    }\n\n    fun Any.shouldNot(matcher: Matcher) {\n        if (matcher.matches(this)) {\n            throw AssertionError("उम्मीद नहीं थी ${matcher.expected}, लेकिन मिला $this")\n        }\n    }\n}\n\nfun test(name: String, block: TestContext.() -> Unit) {\n    println("टेस्ट चल रहा है: $name")\n    try {\n        TestContext().block()\n        println("✓ पास")\n    } catch (e: AssertionError) {\n        println("✗ फेल: ${e.message}")\n    }\n}
\n\n

मुख्य बिंदु: TestContext.() -> Unit - यह रिसीवर के साथ लैम्ब्डा है। ब्लॉक के अंदर this TestContext ऑब्जेक्ट को संदर्भित करता है, इसलिए should और shouldNot मेथड्स अप्रत्यक्ष रूप से उपलब्ध हो जाते हैं।

\n\n

चरण 2. मैचर्स को लागू करें

\n\n

अब सरल मैचर्स बनाते हैं। beEqualTo और beNull ऐसे फंक्शन हैं जो Matcher ऑब्जेक्ट लौटाते हैं:

\n\n
interface Matcher {\n    val expected: String\n    fun matches(value: Any?): Boolean\n}\n\nfun beEqualTo(expected: Any) = object : Matcher {\n    override val expected = "$expected के बराबर"\n    override fun matches(value: Any?) = value == expected\n}\n\nval beNull = object : Matcher {\n    override val expected = "null"\n    override fun matches(value: Any?) = value == null\n}
\n\n

ब्लॉग

पुस्तक अनुशंसाएँ