4 पॉइंट द्वारा GN⁺ 2023-09-18 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • 19 सितंबर 2023 को रिलीज़ हुए Java 21 ने record patterns और switch pattern matching के ज़रिए Kotlin, Rust और C# के करीब functional pattern expressions को Java के भीतर ला दिया
  • Java 14 के switch expressions, Java 16 के records और instanceof pattern matching, और Java 17 की sealed classes के जुड़ते जाने से algebraic data types को संभालने की बुनियाद Java 21 में एक साथ फिट होती है
  • records final, immutable references और standardized getters जैसी सीमाओं के साथ data को भरोसेमंद ढंग से destructure करने देते हैं, और record pattern nested data को switch में सीधे निकालने देता है
  • sealed classes/interfaces केवल अनुमत subtypes को खोलकर sum type के करीब model बनाते हैं, और sealed interface व record को साथ इस्तेमाल करने पर RGB, CMYK, YUV, HSL जैसे variants को सीमित किया जा सकता है
  • Java 21 का switch null case और when guard तक support करता है, लेकिन गलत record accessor या guard execution के दौरान exception java.lang.MatchException तक ले जा सकता है

Java 21 में stable हुआ pattern matching

  • Java 21 19 सितंबर 2023 को रिलीज़ हुआ था, और switch blocks व switch expressions में record patterns support करता है
  • इस syntax को Java में भी Kotlin, Rust और C# जैसे तरीकों से functional programming patterns व्यक्त करने का turning point माना जाता है
  • हाल के Java versions में हुए मुख्य syntax बदलाव Java 21 के pattern matching तक पहुंचे
    • Java 14: switch expressions stable हुए
    • Java 16: records, instanceof pattern matching stable हुए
    • Java 17: sealed classes stable हुईं
    • Java 21: record patterns, switch pattern matching stable हुए
  • इन बदलावों के bundle से Java अब पहले express करना मुश्किल रहे algebraic data types और उनके idiomatic usage patterns को संभाल सकता है

Type theory के लिए ज़रूरी न्यूनतम concepts

  • Java 21 की features समझने के लिए type theory के कुछ concepts ज़रूरी हैं
  • bottom/empty type उन values के set को दिखाता है जिन्हें compute नहीं किया जा सकता, और आम programming languages में यह आम तौर पर empty set होता है
    • Kotlin का Nothing private constructor वाला है, इसलिए उसका instance मौजूद नहीं हो सकता
    • Java का Void private constructor वाला है, लेकिन उसमें null रखा जा सकता है, इसलिए उसे असली bottom type मानना मुश्किल है
    • Java का primitive void variable type के रूप में इस्तेमाल नहीं किया जा सकता, इसलिए इस लिहाज़ से वह ज्यादा करीब व्यवहार करता है
  • top type सभी types की सभी values को दिखाने वाला universal set है
    • Kotlin में Any यह भूमिका निभाता है
    • Java का Object primitive types के object model से अलग होने के कारण दूसरी languages के top type जैसा अर्थ रखना मुश्किल है
  • unit type वह type है जिसमें केवल एक value होती है
    • Java का void method return में unit type की तरह संभाला जा सकता है, लेकिन parameter type के रूप में pass नहीं किया जा सकता
    • Kotlin का Unit object के रूप में define होता है और method parameter के रूप में भी इस्तेमाल किया जा सकता है
  • boolean type में true और false दो values होती हैं, और इसे nullable unit type के रूप में भी express किया जा सकता है, लेकिन यह practical नहीं है

Product type और Java records

  • product type वह type है जो दो या अधिक component types को बांधता है, और component types की संख्या arity या degree बनती है
  • C का struct product type का उदाहरण है
    • int, char *, double, int जैसे component types repeat हो सकते हैं
    • repeated types को field names के साथ ordered pair मानकर अलग पहचाना जा सकता है
  • Python या Rust के tuple को भी product type माना जा सकता है, जहां components के नाम की भूमिका index निभाता है
  • Java 16 में stable हुई record class product type का अच्छा उदाहरण है
    • record के fields final होते हैं, और record को inherit नहीं किया जा सकता
    • record की state creation के समय set होती है और उसके बाद बनी रहती है
    • हालांकि, record के भीतर mutable data type डालने पर उसके contents की immutability तक guarantee नहीं होती
  • सामान्य Java class में public/private state, inheritance से बनी hidden state, mutable/static field और non-standard getter मिल सकते हैं, इसलिए components को generalize करना मुश्किल होता है
  • records नीचे दी गई सीमाओं से ऐसी structure guarantee करते हैं जिसमें pattern matching जैसी language features भरोसेमंद ढंग से काम करती हैं
    • record implicitly final class होता है और inherit नहीं किया जा सकता
    • java.lang.Record के अलावा किसी class को extend नहीं कर सकता
    • record component पर visibility modifier नहीं लगाया जा सकता
    • component reference हमेशा final होता है और immutable माना जाता है
    • default getter field name को ज्यों का त्यों इस्तेमाल करता है, और a field का getter a() बनता है
    • backing field implicitly private होता है और getter के ज़रिए access होता है

record pattern से nested data destructuring

  • Java 21 का switch pattern instanceof checks और explicit casts को दोहराए बिना nested record data को destructure करता है
  • उदाहरण में record A(Record inner), record B(char b), record SomeOtherRecord() इस्तेमाल होते हैं
    • पुराने तरीके में if (r instanceof A) के बाद cast करना पड़ता है, और internal value पर फिर से instanceof और cast दोहराना पड़ता है
    • switch pattern case A(B(char a)) -> String.valueOf(a) की तरह nested value को सीधे extract करता है
  • switch block की structure if-else ladder से ज्यादा clear होती है, और यह deeply nested data को तेजी से निकालने के लिए उपयुक्त है
  • Java 21 में सीधे चलाना हो तो code को main.java में डालकर नीचे की command इस्तेमाल कर सकते हैं
java --enable-preview --source 21 main.java
  • यह उदाहरण unnamed main methods preview feature भी साथ में दिखाता है

Sum type और sealed classes/interfaces

  • सीमित choices express करने के लिए Java enum इस्तेमाल किया जा सकता है, लेकिन RGB, HSL, YUV, CMYK जैसे अलग-अलग data structures वाले color representations को केवल enum से संभालना cumbersome है
  • inheritance-based polymorphism से Color abstract class और RGB, CMYK, YUV, HSL classes बनाई जा सकती हैं, लेकिन सामान्य class hierarchy open होती है
    • library user RYB जैसी नई class बनाकर Color inherit कर सकता है
    • अगर API ने extension का इरादा नहीं रखा था, तो नया variant crash या दूर के code में subtle bugs पैदा कर सकता है
  • sealed classes Java में sum type concept express करने के लिए इस्तेमाल होती हैं
    • sum type ऐसा type है जो एक समय में अपने components में से एक हो सकता है
    • इसे tagged union type भी कहा जाता है
  • sealed modifier और permits clause इस्तेमाल करने पर केवल खास classes को inheritance allow किया जा सकता है
public sealed class Color permits RGB, CMYK, YUV, HSL {
}
  • sealed class hierarchy में direct या indirect inheritor के पास sealed, non-sealed, final में से एक होना चाहिए; नहीं होने पर compile error आता है
    • sealed: केवल permits में नामित types inherit कर सकते हैं
    • non-sealed: सामान्य class की तरह inherit किया जा सकता है
    • final: inheritance tree का leaf, आगे extend नहीं किया जा सकता

sealed interface और record को साथ इस्तेमाल करने का तरीका

  • switch pattern matching की destructuring records पर काम करती है, लेकिन records Record के अलावा किसी class को inherit नहीं कर सकते
  • समाधान sealed interface इस्तेमाल करना है
    • sealed interface, sealed class की तरह ही काम करता है
    • records और enums भी sealed interface को implement कर सकते हैं
  • उदाहरण में Color को sealed interface बनाया गया और RGB, CMYK, YUV, HSL को record के रूप में implement किया गया
public sealed interface Color permits RGB, CMYK, YUV, HSL {
    String getDescription();
}

record RGB(int red, int green, int blue) implements Color {
    public String getDescription() {
        return "RGB Color: (" + red + ", " + green + ", " + blue + ")";
    }
}
  • इसके बाद switch में हर record की values सीधे extract की जा सकती हैं
switch (color) {
  case RGB(int red, int green, int blue) -> {
  }
  case CMYK(double cyan, double magenta, double yellow, double black) -> {
  }
  case YUV(int y, int u, int v) -> {
  }
  case HSL hsl -> {
    System.out.println(hsl.getDescription());
  }
  case null -> {
    System.out.println("How did color become null?!");
  }
}
  • Java 21 switch blocks और expressions में null case handle कर सकता है, इसलिए switch से पहले अलग null check करने की ज़रूरत नहीं होती
  • अगर Color sealed type है, तो Java जान सकता है कि सभी cases handle हुए हैं या नहीं, इसलिए default case के बिना भी exhaustive switch संभव है

Guard clause और when

  • Java 21 switch arm में extra condition जोड़ने वाली guard clause support करता है
  • guard clause when keyword से case label में condition integrate करती है
switch (color) {
  case RGB(int red, int green, int blue) when red > 200 -> {
    System.out.println("Very red.");
  }
  case RGB rgb when rgb.green > 100 -> {
    System.out.println("Sort of green...");
  }
  case RGB rgb -> {
    System.out.println("Not that red...");
  }
}
  • पहले case RGB(...) body के भीतर फिर से if (red > 200) डालना पड़ता था
  • Java true evaluate होने वाले पहले case को eagerly match करता है, इसलिए ज्यादा specific case पहले और कम specific case बाद में रखने चाहिए
  • guard वाले RGB case के बाद exhaustive बनाए रखने के लिए सामान्य case RGB rgb ज़रूरी है

MatchException कब आती है

  • Java 21 के pattern matching से java.lang.MatchException अतिरिक्त रूप से जुड़ा है
  • अगर record accessor exception throw करे, तो switch pattern fail हो सकता है और MatchException आ सकती है
record R(int i) {
    public int i() {
        return i / 0;
    }
}

static void exampleAnR(R r) {
    switch(r) {
        case R(var i): System.out.println(i);
    }
}
  • ऊपर के उदाहरण में i() accessor ArithmeticException throw करता है, इसलिए switch block MatchException throw करता है
  • JEP 441 के अनुसार हमेशा exception throw करने वाला record accessor बेहद असामान्य है, और exhaustive pattern switch का MatchException throw करना भी बेहद दुर्लभ है
  • exhaustive switch में भी अगर selector के लिए specified variants में से कोई match न हो, तो exception आ सकता है
    • JEP 441 enum पर exhaustive switch के matching में fail होने की स्थिति को switch compile होने के बाद enum class बदल जाने की स्थिति के रूप में समझाता है
  • guard clause execution के दौरान exception आने पर भी MatchException आ सकती है
static void example(Object obj) {
    switch (obj) {
        case R r when (r.i / 0 == 1): System.out.println("It's an R!");
        default: break;
    }
}

बाकी दायरा

  • Java 21 के records, sealed types, switch pattern matching और guard clause को मिलाकर functional programming के building blocks Java code में लागू किए जा सकते हैं
  • generics का switch patterns के साथ interaction जैसे कुछ topics यहां cover नहीं किए गए हैं
  • अगले article में Java code लिखने के तरीके को बेहतर बनाने में इस्तेमाल हो सकने वाले quirks और practical examples cover किए जाएंगे

1 टिप्पणियां

 
GN⁺ 2023-09-18
Hacker News की रायें
  • Java 21 की सबसे बड़ी सुविधा virtual threads का रिलीज़ होना है: https://openjdk.org/jeps/444
    पता नहीं क्यों, लेख में यह छूट गया है। अगर कोई ऐसी सुविधा है जो मौजूदा Go developers को Java की ओर खींच सकती है, तो शायद यही हो सकती है, और यह उन लोगों को भी मना सकती है जिन्हें reactive-style concurrency patterns पसंद नहीं थे

    • मुझे नहीं लगता कि मौजूदा Go developers Java पर वापस जाएंगे। मैंने 10 साल Java में काम करने के बाद Go अपनाया, और वापस जाने का कोई इरादा नहीं है
      Java applications और libraries को inheritance, packaging, object-oriented design, build tools वगैरह की वजह से Go की तुलना में reason करना और समझना बहुत मुश्किल है
      Go सरल है और समझने, पढ़ने व maintain करने में आसान है। Packaging वैसी ही लगती है जैसे कंप्यूटर में files को एक ही folder में व्यवस्थित करना, और tools भी language में built-in हैं। ऐसा भी नहीं लगता कि IntelliJ जैसे IDE के बिना यह बस मुश्किल से इस्तेमाल लायक बनता है
      हो सकता है अब चीज़ें बदल गई हों, लेकिन आजकल जो ज्यादातर Java libraries दिखती हैं, वे अब भी ऐसी ही लगती हैं
    • Java 21 की वजह से virtual threads के साथ आने वाली अगली JRuby release का इंतज़ार है। Charles Nutter ने अगस्त की JRuby presentation में Ruby fibers पर इसके असर का demo दिखाया था, और असर काफी बड़ा है
      JVM और उसके tooling ecosystem में मुझे बहुत सी चीज़ें पसंद हैं, लेकिन अब Java code लिखना खास पसंद नहीं है। JRuby दोनों पक्षों की खूबियाँ कुछ हद तक साथ दे देता है
      presentation यहाँ है, और virtual threads demo करीब 45वें मिनट से शुरू होता है
      https://youtu.be/pzm6I4liJlg?si=GtxQ4MThEaNDfC67
    • मुझे पक्का नहीं कि मौजूदा Golang developers सिर्फ इस feature की वजह से Java पर शिफ्ट होंगे। लेकिन दिशा के लिहाज़ से, मुझे हैरानी है कि Go community में concurrency containers इतने कम और Java community में इतने ज्यादा क्यों हैं
      sync.Map तक Java के general-purpose ConcurrentMap जैसा नहीं है, बल्कि दो खास use cases के लिए specialized है। Java में concurrent sets, queues, barriers, phasers, fork-join pools वगैरह हैं। Go routines होने के बावजूद ऐसे containers काफी उपयोगी हो सकते हैं; कम-से-कम fork-join को implement करना इतना मामूली नहीं है। हर जगह mutex इस्तेमाल करना बहुत low-level लगता है
      मुझे पता है कि third-party implementations मौजूद हैं, लेकिन concurrency को सही तरीके से बनाना इतना मुश्किल है कि जब तक कोई third-party package Java के JCTools या Google Guava जितना mature न हो और उसके पीछे बहुत सारे users व developers न हों, उसे अपनाने में हिचक होती है
    • Executor.newVirtualThreadPerTaskExecutor और go का contrast अच्छी तरह दिखाता है कि Go developers Java पर नहीं जाएंगे, ऐसा क्यों लगता है
      सुधार करूँ तो असल में यह go की जगह try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { executor.submit(...) } के ज्यादा करीब है
    • Java 21 structured concurrency भी preview के रूप में देता है(https://openjdk.org/jeps/453). यह virtual threads implementation का उपयोग करता है, और examples देखकर ही काफी अच्छा लगता है; thread-based concurrency संभालते समय आने वाली कई परेशानियाँ कम करता है
  • मुझे लगता है blog post का title अच्छा चुनाव नहीं था। छिपा हुआ subtitle "Algebraic data types in Java" है, और वही content को कहीं बेहतर समझाता है। बेहतर title Algebraic data types in Java 21 होता
    शायद title की वजह से यहाँ की काफी comments topic से भटक गईं। मैं algebraic data types, Java implementation के pros-cons, और दूसरी languages से technical comparison के बारे में ज्यादा देखना चाहता था

    • मैं सोचता हूँ कि काश algebraic data types वाली कोई और popular language होती, लेकिन मुझे सच में नहीं पता कि Java में algebraic data types आते देखना चाहता हूँ या नहीं
      मौजूदा Java code गायब तो नहीं होगा, इसलिए अगर ऐसा code randomly mix होने लगे तो क्या वाकई चीज़ें बेहतर होंगी, इस पर शक है
    • शुरुआत में मैंने title ऐसा ही रखा था, लेकिन आखिरी पल में बदल दिया, और लगता है उसी वजह से लेख की दिशा भटक गई
  • यहाँ समझाई गई sealed classes सुविधा कुछ पूरी तरह गलत-सी लगती है
    तर्क यह है कि अगर कोई सामान्य interface है, तो कोई भी उसे implement करने वाली नई class बना सकता है, और if (x instanceof Foo) { ... } else if (x instanceof Bar) { ... } जैसा code तब runtime पर टूट जाएगा जब कोई नई class जोड़ देगा। इसलिए नई sealed interface सुविधा से अगर किसी को भी उस interface को implement करने वाली नई class बनाने से रोक दिया जाए, तो if statement नहीं टूटेगा
    लेकिन क्या object-oriented programming ने पहले ही इस तरह की समस्या के बारे में सोचकर उसे हल नहीं कर दिया था? मुझे पता है कि object-oriented अब चलन से बाहर हो गया है, लेकिन Java एक object-oriented language है
    समाधान है interface में method जोड़ना और सभी classes से उसे implement कराना। तब सभी विकल्पों को एक विशाल if/switch statement में गिनाने के बजाय method call किया जा सकता है
    यह तरीका code को extend करने से रोकने की तुलना में बेहतर है, और उलटे उसे extend करने देता है। नए implementer को बस वह method implement करना होगा, और compiler इसे enforce करता है, इसलिए गलती से उसे छोड़ा भी नहीं जा सकता
    लेख का color space वाला उदाहरण (RGB, CMYK आदि) बहुत अच्छा counterexample है। अगर मैंने color spaces का उपयोग करने वाला code लिखा है, तो user या customer को कोई ऐसा अजीब और दुर्लभ color space इस्तेमाल करना पड़ सकता है जिसके बारे में मैंने सोचा ही न हो। मैं ऐसा code नहीं बनाना चाहूँगा जो केवल विशाल if/switch statement में सूचीबद्ध color spaces को support करे और उसी structure की वजह से extend न किया जा सके

    • interface में method जोड़ने वाला समाधान तब मुश्किल में पड़ता है जब आप आगे ज़रूरी होने वाले सभी operations पहले से नहीं जान सकते
      sealed classes इस समस्या को हल करती हैं। लेकिन बदले में एक नई समस्या पैदा होती है: “अगर और extension classes की ज़रूरत हो और उन्हें सबको पहले से जानना संभव न हो तो?” आखिर सवाल यह बनता है कि क्या दोनों चीज़ें हासिल करने का कोई तरीका है
      इस समस्या को expression problem कहा जाता है [1]
      कुछ statically typed languages हैं जो expression problem को हल कर सकती हैं, और Java भी उनमें से एक है [2]। हालांकि Java में ऐसा करने का तरीका अब भी बहुत जटिल और असुविधाजनक है, इसलिए इसका इस्तेमाल लगभग नहीं होता। अगर आप Haskell या JVM की दुनिया में रहना चाहते हैं, तो Scala इस मामले में कहीं बेहतर है
      [1] https://en.wikipedia.org/wiki/Expression_problem
      [2] https://koerbitz.me/posts/Solving-the-Expression-Problem-in-...
    • sealed classes वाला तरीका भी किसी को भी code extend करने देता है, लेकिन interface methods वाले तरीके से अलग dimension में extension होता है
      interface methods और virtual calls वाला तरीका तब बहुत flexible नहीं होता जब आप नई classes जोड़ने के बजाय नए operations जोड़ना चाहते हों। सिर्फ एक नया operation जोड़ना हो, तब भी सभी implementations में जाकर नया method डालना पड़ता है, और जिन implementations तक आपकी access नहीं है उन्हें तोड़ भी सकता है। आपस में असंबंधित methods भी एक ही class में define करने पड़ते हैं, जिससे code readability काफी खराब होती है, और virtual call भी मुफ्त नहीं है—performance पर भी असर पड़ता है
      इस मामले में sealed class कहीं बेहतर तरीके से scale होती है। एक जगह नया switch जोड़िए और काम खत्म, backward compatibility भी नहीं टूटती
      यही मशहूर expression problem है
      https://pkolaczk.github.io/in-defense-of-switch/
    • instanceof के बजाय polymorphic dispatch इस्तेमाल करने की सलाह मैं समझता हूँ, और Bob Martin को इस पर लंबी बात करते भी देखा है, लेकिन मैं सहमत नहीं हूँ
      इस तरह का polymorphic dispatch करने के लिए object को कई concerns खुद संभालने पड़ते हैं
      वीडियो गेम में Car के पास .render(), .collide(), .playSound() हो सकते हैं। बाद में Dog जोड़ें तो बस ये तीन methods implement करने होंगे, और Renderer, PhysicsEngine, SoundEngine को बदलने या फिर से compile करने की ज़रूरत नहीं पड़ेगी। दूसरे programmer भी मेरे कीमती code में bug डाले बिना entities जोड़ सकते हैं। सुनने में अच्छा लगता है
      लेकिन अब Car और Dog को graphics, physics और sound—तीनों के बारे में जानना होगा। और entities अलग-थलग मौजूद नहीं होतीं। कार और कुत्ते को सही क्रम में render होना चाहिए और वे एक-दूसरे को ढक भी सकते हैं। collisions भी आपस में check करने होंगे। असली game jam में जैसा अनुभव हुआ, sound वाला व्यक्ति sound behavior जोड़ने के लिए हर object के अंदर जाने पर मजबूर हो सकता है
      बेहतर यह है कि physics के बारे में सोचते समय Physics.collideAll() के अंदर काम किया जाए और ज़रूरत हो तो instanceof से special handling की जाए; और graphics के बारे में सोचते समय Graphics.renderAll() के अंदर काम किया जाए
      रोज़मर्रा के backend Java web development में भी यही बात लागू होती है। REST controller में Java object को HTTP response में बदलने का तरीका तय करते समय, एक ही method में सब कुछ देखना और {instanceof Forbidden} को 403, {instanceof NotFound} को 404 पर map करना बेहतर है। मैं Java class के अंदर ही getCode() या REST-specific चीज़ें नहीं डालना चाहूँगा
    • extension की अनुमति देना हमेशा समझदारी नहीं होता। String के final होने की वजह है, और यहाँ तक कहा जा सकता है कि final को default रखना चाहिए और जिन classes को subclassing की अनुमति देनी हो उन्हें ही साफ तौर पर open mark करना चाहिए
      functional programming में sum type का typical उदाहरण list है। इसमें केवल Element(T head, List tail) और Nil() होते हैं। इसे extend करने की कोई वजह नहीं है, और सच कहें तो extend करने पर list को handle करने वाले सभी functions के साथ मिलकर गलत code बन सकता है
      साथ ही pattern matching जैसा visitor pattern बहुत verbose है और Java के सामान्य method dispatch semantics का उपयोग करने वाले hack पर निर्भर करता है। यहाँ मुझे pattern matching कई गुना ज्यादा readable लगती है
    • ऐसी features के valid use cases हैं
      उदाहरण के लिए, security token validate करने वाला security interface सोचा जा सकता है
      अगर वह सामान्य interface हो, तो उसे implement करके token को ignore करना (सबको allow करना), token चुराना, या backdoor डालना आसान है। अगर ऐसी class उस जगह inject हो जाए जहाँ security check होता है, तो security टूट सकती है
      sealed interface के साथ unauthorized नया implementation मौजूद नहीं हो सकता। अगर आपको कोई object मिलता है जो उस interface को implement करने का दावा करता है, तो यह guarantee होती है कि वह वास्तविक security check करने वाले verified implementations में से एक है। यानी security bugs और exploits की एक पूरी category ही खत्म हो गई
  • sum type के बारे में पता है, लेकिन Java के sum type के बारे में ज्यादा नहीं जानने वाले नज़रिए से यह अच्छा लेख है
    हालांकि मुझे नहीं पता कि सिर्फ sum type की वजह से Java फिर से पसंद आने लगेगा या नहीं। व्यापक nullability अब भी बनी हुई है, और इस लेख में भी यह कई बार सिर उठाती है

    • Java में nullability बड़ी समस्या है, लेकिन annotation-आधारित nullability frameworks प्रभावी हैं और पूरे ecosystem में फैले हुए हैं। निजी तौर पर मैं इसे लगभग अनिवार्य मानता हूँ
      Google, Meta, Microsoft आदि द्वारा @Nullable से शुरू करके annotations को standardize करने की कोशिश https://jspecify.dev/ से मुझे वाकई बहुत उम्मीद है
    • Valhalla आने पर explicit nullability भी आ जाएगी, इसलिए वह समस्या भी संभल जाएगी
    • यह अभी भी expression-oriented language नहीं है
  • "इसे product type क्यों कहते हैं?" के लिए लेख का जवाब गलत नहीं है, लेकिन ज्यादा सहज और संक्षिप्त रूप से कहें तो product type में possible values की कुल संख्या उसके component types की possible values की संख्याओं का गुणनफल होती है
    product को sum से बदल दें तो भी यही बात वैसी ही लागू होती है
    दिलचस्प बात यह है कि a -> b रूप वाले unique functions की कुल संख्या, सिर्फ input और output को देखें तो exponent से निकाली जा सकती है। यानी (b की possible values की संख्या) ^ (a की possible values की संख्या)

    • ज्यादा सहज और संक्षिप्त रूप से कहें तो, product type sets के Cartesian product के बराबर होता है
    • maps और lists भी exponential types के दूसरे उदाहरण हैं। Pure functions को सिद्धांत रूप में precomputed values पर map lookup से बदला जा सकता है, इसलिए इनका functions जैसा होना intuitively स्वाभाविक है। इस context में list एक special map है जिसकी keys integers होती हैं
      गणितीय रूप में लिखें तो, Bool की list में बाईं ओर elements की संख्या है और दाईं ओर कुल possibilities की संख्या है
      0 : 1
      1 : 2
      2 : 4
      3 : 8
      4 : 16
      5 : 32
      इसी तरह आगे चलता है
  • Project Valhalla पूरा होकर Java में आखिरकार value types आने का इंतज़ार कर रहा हूँ। तब sum types, value types और coroutines तक मिलकर यह काफी अच्छी languages में से एक बन सकती है

  • Java मूल रूप से सचमुच खराब language नहीं थी
    समस्या लोग थे। विशाल over-engineering, codebase समझना मुश्किल बनाने वाले बहुत ज्यादा abstract concepts, reverse GOTO statements जैसे annotation-form code magic, और DI frameworks समस्या थे
    जिसे ठीक करने की जरूरत है वह language नहीं, बल्कि ecosystem है। Java ecosystem के अंदर एक तरह के "Reformation" movement की जरूरत है
    सिर्फ Kotlin, Clojure, Scala पर चले जाना पर्याप्त नहीं है

    • किसी भी language में HammerFactory बनाने वाली HammerFactoryFactory बनाई जा सकती है। लेकिन Java ecosystem इस तरह की problem-solving approach को बढ़ावा देता और encourage करता है। C# भी मुझे कुछ वैसा ही लगता है
      Java को सच में जिस एक चीज़ की जरूरत है, वह independent functions, या namespaced functions हैं। कभी-कभी class की जरूरत नहीं होती और module या namespace के अंदर function ही काफी होता है, लेकिन यह क्यों नहीं हो सकता, समझ नहीं आता
  • लेखक Records की जरूरत समझाते हुए बताते हैं कि ज्यादातर Java objects सभी fields को private रखते हैं और सिर्फ read/write accessor methods से access कराते हैं
    लेकिन accessors define करने के लिए language-level enforced convention नहीं है, इसलिए foo के getter का नाम getBar रख दें तो वह काम तो करेगा, लेकिन bar access करने की कोशिश करने वाले व्यक्ति को confuse कर सकता है
    Scala unapply method implement करने वाले objects पर pattern matching support करता है। क्या इस तरीके को harmful माना जाता है? Java ने यह रास्ता क्यों नहीं अपनाया?

    • यह फिर standardization की समस्या है। Java की standardization C++ जैसी धीमी है। record pattern JEP के आखिरी footnote को देखें तो संकेत मिलता है कि unapply जैसा कुछ तैयार हो सकता है, इसलिए उम्मीद अभी पूरी तरह खत्म नहीं हुई है
  • Java हमेशा से शानदार language रही है। उल्टी जैसा महसूस कराने वाली चीज़ enterprise-style ecosystem है। मैंने एक लाइन की logic implement करने के लिए दर्जनों classes और interfaces इस्तेमाल होते देखे हैं