1 पॉइंट द्वारा GN⁺ 2024-08-04 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • Java टाइप्स पर nullness markers जोड़कर Foo! के जरिए null को अस्वीकार करने और Foo? के जरिए उसे जानबूझकर स्वीकार करने वाली एक preview language feature तैयार कर रहा है
  • बिना किसी marker वाला Foo nullness के लिहाज़ से unspecified रहेगा, ताकि पुराने कोड के साथ compatibility बनी रहे; और अलग nullness वाले types के बीच conversion पर warning या runtime checks लग सकते हैं
  • null को अस्वीकार करने वाले fields और arrays default value null का उपयोग नहीं कर सकते, इसलिए instance fields को super(...) call से पहले definitely assigned होना होगा और arrays को component initial value चाहिए होगी
  • जब null को Foo! में narrow किया जाएगा तो NullPointerException होगा, और array/field store paths में ArrayStoreException और FieldStoreException के जरिए pollution रोका जाएगा
  • Valhalla की value class flattening जैसी optimizations के लिए null exclusion पर भरोसा कर पाना बुनियादी शर्त है, लेकिन standard library पर इसे लागू करना या पुराने कोड को अपने-आप दोबारा interpret करना अभी लक्ष्य नहीं है

Java types में nullness को स्पष्ट करने वाली preview feature

  • Java types पर nullness markers लगाकर यह बताया जाता है कि type के value set में null शामिल है या नहीं
  • Foo! एक null-restricted type है, जो value set से null को बाहर रखता है
  • Foo? एक nullable type है, जो value set में null को जानबूझकर शामिल करता है
  • बिना marker वाला Foo nullness के लिहाज़ से unspecified है; इसमें null आ सकता है, लेकिन यह नहीं पता चलता कि वह इरादतन है या नहीं
  • यह feature एक preview feature है, जो --enable-preview compile और runtime flags से सक्रिय होती है

लक्ष्य और गैर-लक्ष्य

  • Java reference types में यह व्यक्त करना कि वे null की अपेक्षा करते हैं या नहीं, और अलग nullness वाले types के बीच conversion पर warning और checks देना
  • मौजूदा Java code के साथ compatibility बनाए रखना, और source या binary incompatibility पैदा किए बिना इसे धीरे-धीरे अपनाया जा सके
  • null को अस्वीकार करने वाले type की variables को पहली बार पढ़े जाने से पहले initialize होना चाहिए, और अलग से compile की गई classes में भी runtime पर null rejection लागू होना चाहिए
  • Valhalla की value class flattening जैसी runtime optimizations को null-restricted types पर भरोसा करने लायक metadata और integrity guarantees देना
  • अभी जिन चीज़ों को लक्ष्य नहीं बनाया गया है, वे हैं:
    • मौजूदा code को अपने-आप दोबारा reinterpret नहीं करना
    • हर संभावित null को compile error नहीं बनाना
    • int जैसे primitive types के लिए nullable रूप नहीं जोड़ना
    • इस चरण में standard library पर language extension लागू नहीं करना

इसकी ज़रूरत क्यों है

  • Java में String variable या तो String object reference रख सकता है या null, लेकिन language level पर यह बताने का तरीका नहीं है कि इनमें से कौन-सा इरादा है
  • बहुत-से programs मान लेते हैं कि null नहीं होगा, लेकिन Javadoc specification और implementation code में इसे लगातार enforce करने के लिए अतिरिक्त मेहनत चाहिए
  • जब यह अपेक्षा टूटती है, तो null implementation code के भीतर बहता हुआ उस जगह exception पैदा कर सकता है जो मूल bug से काफ़ी दूर हो
  • अगर developer type के हिस्से के रूप में null को reject या accept करने का इरादा व्यक्त करे, तो compile-time feedback और runtime checks अनपेक्षित null को जल्दी पकड़ने में मदद करते हैं
  • Valhalla में value class type variables को values की flattened representation के रूप में optimize किया जा सकता है, लेकिन अगर null encode करने के लिए extra bit चाहिए हो तो memory usage बढ़ सकता है या storage optimization असंभव हो सकती है
  • Amber में pattern match candidates की nullness, switch की exhaustiveness पर असर डाल सकती है, और type pattern की nullness इस बात पर असर डाल सकती है कि null match होगा या नहीं

nullness marker syntax और type structure

  • nullness को type का मूल हिस्सा माना जाता है, इसलिए Foo? और Foo nullness के हिसाब से अलग types हैं
  • array type और array component type दोनों पर nullness markers लग सकते हैं
    • Foo?[]! ऐसा type है जिसमें array खुद null-restricted है, और components nullable Foo हैं
    • multi-dimensional arrays में हर bracket pair के बाद marker लगाया जा सकता है, और परंपरा के अनुसार इसे बाएँ से दाएँ outer से inner की ओर पढ़ा जाता है
  • parameterized types और type arguments पर भी nullness markers लग सकते हैं
    • Predicate!<Foo?> एक null-restricted Predicate है, और type argument nullable Foo है
  • null-restricted या nullable type व्यक्त करने के लिए source में ! या ? का स्पष्ट रूप से होना ज़रूरी है
  • आगे चलकर यह देखा जा सकता है कि class या compilation unit के सभी types को default रूप से null-restricted माना जाए और केवल ? को exception की तरह लिखा जाए, लेकिन इसकी details अलग काम का विषय हैं

field और array initialization rules

  • मौजूदा Java में reference type fields और array components की default value null होती है, लेकिन null-restricted fields या array components के लिए यह शुरुआती value उपयुक्त नहीं है
  • null-restricted fields और arrays को पढ़े जाने से पहले program को हमेशा initialize करना होगा
  • अगर null-restricted instance field में initializer नहीं है, तो हर constructor में explicit या implicit super(...) call से पहले उसका definite assignment होना चाहिए
    • Flexible Constructor Bodies JEP constructor की शुरुआत में ज़रूरी initialization code लिखने की सुविधा देता है
    • इस early construction context में this को refer करना या uninitialized fields को पढ़ने का जोखिम पैदा करने वाले operations की अनुमति नहीं है
  • अगर null-restricted instance field में initializer है, तो वह हर constructor की शुरुआत में super(...) call से पहले चलेगा
    • this(...) call करने वाला constructor, मौजूदा नियमों की तरह, initializer न चलाने वाला एक special case है
  • null-restricted static fields को class के सभी static initializers और initialization blocks खत्म होने तक definitely assigned होना चाहिए
    • अगर कोई दूसरी class class initialization के दौरान उस field को पढ़ने की कोशिश करे, तो runtime check early read पकड़कर exception फेंकेगा
  • null-restricted component type वाले arrays में array creation expression के समय हर component की initial value देनी होगी
    • array initializer के जरिए सभी values सूचीबद्ध की जा सकती हैं
    • नई short syntax भी संभव है, लेकिन उसका syntax अभी TBD है

expression nullness और nullness conversions

  • Java compiler type checking के दौरान हर expression की nullness तय करता है
  • variable reference की nullness variable declaration से आती है, और method call की nullness referenced method के return type से आती है
  • null literal nullable होता है
  • बाकी ज़्यादातर reference-type expressions null-restricted होते हैं
    • इनमें literals, string concatenation, this, class instance creation, array creation, method references, और lambda expressions शामिल हैं
  • nullness conversions assignment, invocation, और casting contexts में मान्य हैं
  • widening nullness conversion में ये शामिल हैं
    • Foo!Foo?
    • Foo! → unspecified Foo
    • Foo? → unspecified Foo
    • unspecified FooFoo?
  • narrowing nullness conversion में ये शामिल हैं
    • Foo?Foo!
    • unspecified FooFoo!
  • narrowing nullness conversion को compiler unboxing conversion की तरह अपने-आप कर सकता है, लेकिन runtime पर dynamic check होगा और NullPointerException आ सकता है
  • null literal को सीधे null-restricted type में convert करने की कोशिश compile-time error है

runtime checks और exceptions

  • runtime पर अगर null value को narrowing nullness conversion के जरिए null-restricted type में बदला जाता है, तो NullPointerException होता है
  • source code में साफ़ तौर पर न दिखने वाले narrowing nullness conversions भी runtime execution के दौरान हो सकते हैं
  • null-restricted component type वाले array को source code में कम specific type की तरह handle किया जाए, तब भी सामान्य array store check null value को reject करेगा
    • यह conversion failure ArrayStoreException पैदा करता है
  • अगर compile time पर null-restricted न रहा field बाद में अलग compilation के कारण null-restricted बन जाए, तो नया field store check null store को reject करेगा
    • यह conversion failure FieldStoreException पैदा करता है
  • overriding संबंध वाले method calls में, पहले super method parameter call type में conversion हो सकता है और उसके बाद overriding method parameter type में conversion हो सकता है
  • method return value भी पहले declared method return type में convert हो सकती है, फिर call site के expected return type में convert हो सकती है

generics, type arguments, overriding

  • type variable के उपयोग पर भी nullness marker लगाया जा सकता है; T! null-restricted type है और T? nullable type है
  • null-restricted और nullable type-variable types, generic code के भीतर खास nullness assert करते हैं
  • type argument के रूप में उपयोग होने वाले types भी nullness व्यक्त कर सकते हैं, और type-variable type पर लगा nullness marker, type argument में assert की गई nullness को override कर देता है
  • generic API के erased implementation के भीतर null restriction enforce नहीं किया जा सकता
    • लेकिन generic API boundaries पर होने वाले सामान्य implicit casts runtime पर null-restricted type arguments को enforce करते हैं
  • interoperability के लिए type arguments के भीतर की nullness को सख़्ती से enforce नहीं किया जाता
    • Predicate<String!> को Predicate<String> या Predicate<String?> में convert किया जा सकता है
    • ऐसी unchecked nullness conversion warning पैदा कर सकती है
  • array component type की nullness बदलना भी unchecked nullness conversion के रूप में स्वीकार है, और किन परिस्थितियों में runtime check होगा यह अभी TBD है
  • method signature identity तय करते समय nullness को नज़रअंदाज़ किया जाता है
    • parameter और return type की nullness मेल न खाने पर भी एक method दूसरे method को override कर सकता है
    • अगर अलग-अलग APIs स्वतंत्र रूप से nullness markers अपनाएँ, तो ऐसे mismatch आम हो सकते हैं
  • nullness method applicability पर असर नहीं डालती और type argument inference failure का कारण नहीं बन सकती, लेकिन generic method के return type के लिए inferred nullness पर असर डाल सकती है
    • inference algorithm की details अभी TBD हैं

compiler warnings और errors

  • किसी type को null-restricted बनाने पर नए compile-time errors आ सकते हैं
    • जब उस type के fields या arrays initialize न किए गए हों
    • जब null literal को उस type में convert करने की कोशिश हो
    • null literal की तुलना null-restricted type expression से करना भी compile-time error बन सकता है
  • इसके अलावा बाकी स्थितियों में nullness analysis सहायक है और compile-time errors नहीं बनाती
  • javac runtime errors से बचाने के लिए warnings देगा, और IDE व दूसरे analysis tools को भी यही दिशा अपनाने की सलाह है
  • warnings के संभावित कारण ये हैं
    • narrowing nullness conversion, जिसमें ? type से आने वाले cases भी शामिल हैं
    • ? type expression को member access या दूसरे null-hostile operations में इस्तेमाल करना
    • type arguments की nullness का bounds से मेल न खाना
    • method parameter या return की nullness का overridden method से मेल न खाना
    • type की nullness बदलने वाली unchecked conversion

class files, reflection, और सहायक बदलाव

  • null markers के अधिकांश उपयोग class file में मिट जाते हैं, और उनसे जुड़े runtime conversions सीधे bytecode में व्यक्त होते हैं
  • Signature attribute syntax को update किया जाता है ताकि type के भीतर ! और ? की अनुमति हो
  • nullness को method और field descriptor में encode नहीं किया जाता
  • field pollution रोकने के लिए नया NullRestricted attribute यह बताता है कि field null value स्वीकार नहीं करता
    • इस field पर ACC_STRICT भी होना चाहिए, और इसका strict initialization होना चाहिए
    • verifier जाँचता है कि constructor जब super(...) call करे, तब तक सभी strict-initialized instance fields assigned हों
    • field में लिखने की हर कोशिश null value की जाँच करेगी, और मिलने पर FieldStoreException फेंकेगी
  • null-restricted array creation को anewarray instruction support नहीं करता; इसे reflection API call से करना होगा
  • Foo!.class या Foo?.class जैसे literals नहीं होंगे, और उनके लिए कोई java.lang.Class instance भी नहीं होगा
  • नया RuntimeType API उन type sets को वर्णित करता है जिन्हें runtime पर array और field store checks enforce करते हैं, और इसमें सभी class व interface types के null-restricted variants शामिल हैं
  • Field API field के RuntimeType की query support करेगा, और यह value getType के result से अलग हो सकती है
  • Array API, component type को RuntimeType के रूप में व्यक्त करने वाले newInstance variant को support करेगी
    • यह variant array component की initial value देने की अनुमति भी देगा
    • बिना initial value के null-restricted array creation की कोशिश reject की जाएगी
  • पारंपरिक deserialization, null-restricted fields और arrays के साथ compatible नहीं है; एक अलग JEP ऐसा serialization mechanism देगा जो uninitialized null-restricted fields और arrays को expose नहीं करेगा
  • javadoc द्वारा generated documents में nullness markers शामिल होंगे
  • java.lang.reflect.Type और javax.lang.model APIs type representation में nullness को encode करेंगी

alternatives और dependencies

  • Java ecosystem के कई developer tools ने अपनी compile-time null tracking बनाई है, लेकिन Java language को बदले बिना, इसलिए syntax ज़्यादातर annotations तक सीमित रहती है और असर डाल सकने वाला व्यवहार भी compile-time checks तक सीमित रहता है
  • दूसरी languages type system में nullness track करती हैं, और कई languages default रूप से null-restricted होती हैं तथा explicit null check के बिना null-restricted type में assignment को error मानती हैं
  • Java में यह feature optional होनी चाहिए और एक बड़े migration के बिना धीरे-धीरे इस्तेमाल की जा सके
  • runtime nullness enforcement को explicit checks या Objects.requireNonNull calls से भी लागू किया जा सकता है
    • लेकिन इसे लगातार लागू करना झंझट भरा है, अतिरिक्त documentation मांगता है, और program readability घटाता है
    • fields और arrays जैसे variable storage पर इसे सीधे लागू करने का तरीका नहीं है
  • पूर्व-आवश्यकता Flexible Constructor Bodies (Second Preview) है
    • इससे constructors super(...) call से पहले statements चला सकते हैं और instance fields assign कर सकते हैं, जिससे null-restricted field initialization requirement संभव होती है
  • आगे के काम में ये शामिल हैं
  • अतिरिक्त संभावित future improvements में standard API के कुछ हिस्सों पर nullness markers लागू करना, bytecode में concise null check representation, null-restricted method parameters के लिए अधिक मजबूत low-level enforcement, और ऐसी language mechanisms शामिल हैं जो कुछ contexts में सभी types को implicit रूप से null-restricted मान लें

1 टिप्पणियां

 
GN⁺ 2024-08-04
Hacker News की राय
  • यह दिलचस्प है कि यह approach कुछ साल पहले C# द्वारा अपनाए गए तरीके से कैसे अलग है। C# में, अगर आप किसी project में nullability चालू करते हैं, तो जब तक किसी variable को स्पष्ट रूप से nullable न बताया जाए, सभी variables non-null घोषित माने जाते हैं। इस proposal में मौजूदा variables व्यावहारिक रूप से तीन में से एक हो जाते हैं: nullable, explicitly nullable, या explicitly non-nullable
    Kotlin भी JVM language है, लेकिन C# की तरह, अगर स्पष्ट न किया जाए तो उसे non-null मानता है; हालांकि Kotlin पर backward compatibility का बोझ नहीं है। lateinit var के जरिए non-nullable type को किसी दूसरे method में initialize होने तक खाली छोड़ा जा सकता है, और initialization से पहले access करने पर dedicated exception फेंकने वाला workaround भी देता है
    मैं सोच रहा हूं कि तीन विकल्प क्यों रखे गए हैं। क्या unannotated variables को nullable बनाए रखना और सिर्फ annotated variables को explicit non-null बनाना पर्याप्त नहीं होता? अगर कोई marker न होने पर भी वह अपने आप nullable है, तो nullable घोषित करने की वजह समझ में नहीं आती
    मुझे C# वाला तरीका ज्यादा पसंद है, लेकिन इस तरीके का फायदा यह है कि इसे legacy codebase में nullability की सारी समस्याएं हल किए बिना भी इस्तेमाल किया जा सकता है। उलटे, C# nullability की समस्याओं को तुरंत सामने ला देता है, जबकि यह proposal उन्हें अब तक की तरह छिपाए रख सकता है
    और “एक method दूसरे method को override कर सकता है, भले ही उसके parameters और return value की null स्थिति मेल न खाए” वाला हिस्सा अजीब है। callback को override/implement करते समय अगर original method ने non-null return तय किया हो और implementation null return कर दे, तो यह footgun बन सकता है

    • हमारी कंपनी में अभी हम legacy C# codebase को nullable reference types पर migrate कर रहे हैं, और अगर explicit non-nullable marker होता तो यह बहुत अच्छा होता, क्योंकि तुरंत पता चल जाता कि किन हिस्सों की पहले ही समीक्षा करके annotate किया गया है
      समय की वजह से अभी हमने कुछ key जगहों को छोड़कर सिर्फ annotations चालू करने का रास्ता चुना है, लेकिन जिन जगहों पर conscious decision लिया गया है उन्हें तुरंत देखने के लिए हम JetBrains के NotNullAttribute और CanBeNullAttribute को अभी भी markers के रूप में रखते हैं। दूसरा वाला हटाया जा सकता है क्योंकि nullable के लिए explicit marker है, लेकिन पहले वाले का C# की अपनी feature से नाम clash होता है
      उस लिहाज से तीन विकल्प काफी वांछनीय हैं। वजह यह है कि अगर code सैकड़ों हजार lines का हो, तो migration तेजी और आसानी से करना मुश्किल है
      दूसरे internal projects में, जिस code को छूते हैं उसके आसपास #nullable enable डालकर धीरे-धीरे nullability का scope बढ़ा रहे हैं। यह शर्त भी रखी है कि नया code nullable context में होना चाहिए। यह भी पहले से annotated हिस्सों को explicit करने के लिए ठीक है, लेकिन यह तरीका काफी छोटे codebase और team में ही संभव है
    • Kotlin में भी ऐसे types होते हैं जिनकी nullability तय नहीं होती, हालांकि उन्हें सीधे define नहीं किया जा सकता। Kotlin इन्हें platform types कहता है, और errors व diagnostics में String! की तरह exclamation mark से दिखाता है
      “platform type” शब्द और exclamation mark symbol थोड़े confusing हैं, लेकिन इनके अलावा Kotlin का तरीका काफी अच्छा काम करता है। Kotlin में शुरुआत से nullability थी, इसलिए programmers platform types को सीधे specify नहीं कर सकते, लेकिन JVM या JavaScript जैसे underlying platforms के साथ compatibility के लिए ये अब भी जरूरी हैं, जहां nullability अस्पष्ट होती है
      इस approach में default फिर भी reasonable है। Default हमेशा non-nullable होना चाहिए, और जो लोग अलग सोचते हैं उन्होंने Tony Hoare से कुछ नहीं सीखा। साथ ही backward compatibility भी बनी रहती है। Kotlin के लिए यह आसान था, जबकि Java और C# को मौजूदा source code के साथ compatibility भी बनाए रखनी है
      Java का तरीका भी ideal नहीं है और C# का भी नहीं। C# का तरीका compiler flag के आधार पर code behavior को काफी बदल देता है, और Java का तरीका default को सबसे खराब विकल्प बना देता है
      फिर भी मैं C# की तरफ ज्यादा झुकता हूं। क्योंकि अगर “शायद null हो सकता है” को सबसे आसान विकल्प बना दिया जाए, तो ज्यादातर programmers default के तौर पर वही चुनेंगे। खासकर Java जैसी enterprise-friendly language में ऐसा और भी संभव लगता है। linters और compiler warnings लंबी अवधि में मदद करेंगे, लेकिन लगता है कि ज्यादातर Java code में nullability को ठीक से annotate होने में कई साल लगेंगे। C# users को short term में ज्यादा दर्द होगा, लेकिन explicit nullability के लक्ष्य तक वे शायद काफी तेजी से पहुंचेंगे
      https://kotlinlang.org/docs/java-interop.html#null-safety-an...
      https://www.infoq.com/presentations/Null-References-The-Bill...
    • मैं सहमत हूं कि यह अजीब है। मेरी अपेक्षा यह थी कि source code level पर T? का मतलब nullable और T! का मतलब non-nullable हो, और नए compile होने वाले source में साधारण T का default source file के हिसाब से, package-info file के हिसाब से, या compiler switch के जरिए globally तय करने के लिए कोई pragma जैसा declaration हो
      बाद के किसी LTS Java release में global default बदला जा सकता था। इससे project migration आसान होता, pragma को automatically insert/update किया जा सकता, और जरूरत हो तो पुराना default भी बनाए रखा जा सकता था
      साथ ही अगर मौजूदा JSR-305 @Nonnull annotation को class file में non-nullable type occurrence दिखाने के तरीके के रूप में इस्तेमाल किया जाता, तो पुराने JDK के साथ two-way compatibility भी दी जा सकती थी
    • अंत में एक section है: “ऐसा mechanism देना जिससे language यह assert करे कि किसी खास context में सभी types implicitly null-restricted हैं, भले ही programmer explicit ! symbol न लिखे”
      compiler flag या module tag जैसा कुछ भी हो तो ठीक लगेगा
    • वजह शायद code backward compatibility है, जिसे Java बाकी languages की तुलना में कहीं ज्यादा महत्व देता है
      फिर भी मुझे लगता है कि एक compiler flag आएगा, जो अलग से specify न होने पर automatically non-nullable assume करेगा
  • अच्छा लग रहा है। आखिरकार भाषा-स्तर पर हज़ारों गैर-ज़रूरी exceptions और null checks हटाने का तरीका मिल रहा है। हालांकि null-ness को narrow करने वाला automatic conversion कुछ गलत-सा लगता है
    proposal के उदाहरण String? id(String! arg) { return arg; }, String s = null;, Object! o1 = s; // NPE, Object o2 = id(s); // NPE, Object o3 = (String!) s; // NPE में कम-से-कम पहले दो मामले compile error होने चाहिए, ऐसा लगता है
    आखिरी वाला explicit है इसलिए थोड़ा अस्पष्ट है, लेकिन बेहतर होगा कि if (s != null) के अंदर compiler effective type को String! माने और String! ss = s; की अनुमति दे। तब error की संभावना नहीं रहेगी

    • industry में Java इस्तेमाल करने के अनुभव से कहूँ तो dynamic checks सिर्फ explicit रूप से मांगे जाने पर ही हों और बाकी सब static रूप से handle हो, यह बेहतर है। Bean Validation इसलिए अच्छा काम करता है क्योंकि object अस्थायी रूप से गलत हो सकता है, लेकिन जिस पल आप explicit validation करते हैं, या framework मेरे code में आने से पहले validation करता है, उस पल वह valid होता है
      सच कहूँ तो आखिरी case के cast से ज्यादा मुझे Objects.requireNonNull(s) इस्तेमाल करना पसंद है, क्योंकि वह ज्यादा explicit है। हालांकि Objects.unsafeForceNonNull(s) जैसा कुछ भी अच्छा होगा, जो explicit check को bypass करे लेकिन optimization की वजह से रुकने वाले मामलों को ही छोड़ दे। unsafe method हो तो जटिल static analysis जोड़े बिना सीधे requireNonNull implement किया जा सकता है
    • इसमें लिखा है कि “program से हर संभावित null value को explicit रूप से handle करवाना लक्ष्य नहीं है, और unhandled null value compile time पर warning हो सकती है लेकिन error नहीं”
      अफसोस, यह सिर्फ runtime पर ही पता चलेगा
    • अगर पहला या दूसरा उदाहरण compile error बन जाए, तो इसका मतलब होगा कि हर library usage point पर annotation लगाना पड़ेगा। जब तक वह library null types में migrate नहीं करती, या अगर कभी नहीं करती, तो लगभग हर line casts से भर जाएगी। यह बात समझ में नहीं आती
      उदाहरण के लिए standard library ने साफ कहा है कि कम-से-कम फिलहाल वह null types में migrate नहीं करेगी
    • अगर समस्या की जड़ API में null introduce करना है, तो मुझे पक्का नहीं कि वह भाषा की गलती है या नहीं। यह verbose है और साफ-सफाई के लिए इसकी जरूरत समझ में आती है, लेकिन आखिर समस्या ऐसे code लिखने वाले developers में ही नहीं है क्या?
    • अगर दूसरा मामला compile error नहीं है, तो legacy code non-null argument लेने वाले function को call कर सकेगा, लेकिन यह जरूरी तौर पर अच्छा है या नहीं, पता नहीं। असल में इस्तेमाल करने से पहले फैसला करना मुश्किल है
  • package या कम-से-कम file स्तर पर सभी variables को default non-null mark करने का तरीका जरूर चाहिए लगता है। वरना safety के कारण लगभग हर variable पर T! syntax इस्तेमाल करने की दलील मजबूत होगी, और इससे सिर्फ काफी noise पैदा होगा

    • “भविष्य के संभावित सुधार” के तहत यह है: “ऐसा mechanism देना जिससे programmer को explicit ! symbol लिखने की जरूरत न हो और भाषा यह assert कर सके कि किसी खास context के सभी types implicitly null-restricted हैं”
    • यह इतना बुरा नहीं लगेगा। हमारे project में पहले से standard है कि हमारे स्वामित्व वाले Java code के सभी variables null नहीं होते। अगर nullable variable चाहिए तो @Null से annotation लगाना पड़ता है
      समस्या सिर्फ libraries के साथ interact करने वाली code boundary पर आती है। यह नया syntax भी शायद वैसा ही होगा
    • aggressive linter हो तो unmarked type को non-null मान सकता है और सिर्फ जरूरत वाली जगहों पर nullability marking मांग सकता है
  • “फिलहाल language improvement को standard library पर लागू करना लक्ष्य नहीं है” वाला हिस्सा निराशाजनक है
    PHP को मजबूरी में इस्तेमाल करने के अनुभव से, data के बारे में पहले से guarantee की गई properties को विशाल standard library से interact करते समय हर बार उतारना या फिर से चढ़ाना झंझट है
    Java को भी ऐसी expressiveness को standard library में और सक्रिय रूप से डालकर first-class citizen बनाना चाहिए

    • Java की सबसे बड़ी समस्याओं में से एक यह है कि incremental improvements करते-करते, नया थोड़ा बेहतर code अगली incremental feature के समय फिर legacy code बन जाता है। Optional इस्तेमाल करने वाला legacy code explicit nullable/non-nullable proposal पर बोझ बनता है। लगता है record types भी default non-nullable हो सकते थे
    • लगता है आप “फिलहाल” को “कभी नहीं” की तरह पढ़ रहे हैं, जबकि असल में वह migration खुद एक बहुत बड़ा काम है, इसलिए उसे अलग से करने जैसा है
      साथ ही दो चरणों में बाँटने से इस feature को preview feature के तौर पर ज्यादा आसानी से release किया जा सकता है, feedback लेने के बाद design को निश्चित रूप से final किया जा सकता है। अगर सब कुछ एक साथ करने की कोशिश करें तो feature को असल में iterate करके सुधारने की गुंजाइश लगभग नहीं बचेगी
    • मुख्य शब्द “फिलहाल” है। feature settle हो जाए तो मुझे लगता है यह goal बनेगा
    • मजबूरी कैसी, modern PHP तो काफी comfortable है
  • Java में यह feature आ जाए तो अच्छा होगा। T? जैसी language-level explicit optionality ने Kotlin और TypeScript में developer quality of life काफी बढ़ाई है। Java में NullAway जैसे tools हैं, लेकिन वे झंझट वाले हैं
    मुझे लगता है language-level support Optional/Maybe से कहीं बेहतर है। क्योंकि यह code को map/flatMap की रेल पर चढ़ाने के बजाय actual logic पर focus करने देता है
    https://github.com/uber/NullAway

    • 30 साल से ज्यादा की source compatibility तोड़ने की बात कर रहे हो? ऐसा क्यों करना चाहिए
      बस कोई दूसरी JVM language इस्तेमाल कर लेना बेहतर नहीं होगा?
  • site अभी down है, इसलिए archive link दे रहा हूँ: https://web.archive.org/web/20240802081039/https://bugs.open...

    • सबके प्यारे “jira” पर बदल दिया, इसमें कोई हैरानी नहीं
  • “प्रोग्राम को सभी संभावित null values को स्पष्ट रूप से handle करने के लिए मजबूर करना लक्ष्य नहीं है, और unhandled null values compile time पर warning हो सकती हैं, लेकिन error नहीं” — यह एक खराब फैसला है
    Java ज़्यादातर statically typed language है, फिर इसमें एक और dynamic behavior क्यों डाला जा रहा है, समझ नहीं आता। उम्मीद है कि ऐसी warnings को errors में बदलने का कोई आसान तरीका होगा

    • ऐसा नहीं किया जा सकता। ऐसा करने पर मौजूदा सभी Java programs टूट जाएंगे। अगर इसे enforce किया गया, तो हर program को हर जगह के null responses को handle करने के लिए फिर से लिखना पड़ेगा। इस नए feature के साथ सिर्फ नए types से जुड़े operations पर ही अनिवार्य null handling लागू की जा सकती है, इसलिए कुछ भी नहीं टूटेगा
      सभी मामलों में null/nil handling को type system द्वारा enforce करने वाली language कहीं बेहतर होती है। लेकिन मौजूदा Java ऐसी language नहीं है। फिर भी यह एक बड़ा improvement होगा
  • अफसोस है कि ये सबक इतनी देर से सीखे जा रहे हैं। default रूप से non-nullable, default रूप से immutable, और default रूप से सबसे narrow scope होना चाहिए
    नए design में बहुत बार “safe path पर fall back कराने” के बजाय तुरंत सुविधा को चुना जाता है। Safe defaults के लिए कहीं ज़्यादा सावधान design और user experience चाहिए, लेकिन नतीजा यह हुआ कि लगभग हर language, platform और technology में footguns भर गए। Civil और electrical engineering में नियम-कायदे हैं, लेकिन software हर करीब 30 साल में नई languages और technologies में वही सबक फिर से सीखता है

    • दुर्भाग्य से Java ने शायद अभी तक default non-nullable नियम नहीं सीखा है। यह proposal दो नए types introduce करता है, लेकिन default unannotated type nullable है या नहीं पूछने पर जवाब अब भी “कौन जाने” ही है
    • दुख इस बात का है कि Java ऐसी बातें देर से सीख रहा है। “हम” और “Java” एक चीज़ नहीं हैं
      Java उस तीसरी दुनिया के गांव जैसा है जो यह सीख रहा है कि germs मारने के लिए पानी उबालना पड़ता है
  • Facebook में मैंने जो काम किया, उसका बड़ा हिस्सा Hack इस्तेमाल करना था। nullability Hack type system का core element है और यह सच में बहुत सारी बेकार errors सुलझा देता है
    बेशक इसका मतलब यह नहीं कि कभी unexpected null नहीं मिलेगा। क्योंकि यह feature भी language में बाद में जोड़ा गया था, इसलिए PHP roots को reflect करने वाला legacy mixed type अब भी बहुत था, जिसका मतलब असल में कुछ भी हो सकता था
    पहले, nullable arrays का क्या होगा यह जानना चाहता हूं। String![] example दिखाता है कि object null हो सकता है, लेकिन array खुद का क्या? Java में String labels[] = null; पूरी तरह legal है। तो क्या इसे String![]! labels; की तरह declare करना होगा?
    Hack में vec $foo का मतलब है कि foo भी और elements भी null नहीं हैं, और ?vec $foo में elements non-null हैं लेकिन foo nullable है। असल में null array इस्तेमाल करने की वजह बहुत कम होती है, इसलिए default null-disallowed होना चाहिए। हालांकि Java में समस्या यह है कि पूरा legacy code nullability assume करता है
    Proposal के Object! o1 = s, Object o2 = id(s), Object o3 = (String!) s examples में लगता है कि 2 और 3 compile errors होने चाहिए
    आखिर में, Java की casting की तुलना में Hack का as coercion operator मुझे ज़्यादा पसंद है। उदाहरण के लिए foo($b) compile error है, $b as A अगर null हो तो runtime error देता है, और $a as ?B अगर B हो तो cast करता है, नहीं तो null return करता है
    अंत में सवाल यही बनता है कि क्या Java SDK पर इसे overlay किया जा सकता है, और legacy code में यह कैसा दिखेगा
    https://docs.hhvm.com/hack/types/nullable-types

    • शायद यह बस String![]? labels = null; होगा
  • लगता है Kotlin की अच्छी चीज़ें अब Java में भी आ रही हैं
    फिर भी मैं Kotlin में ही काम करते रहना चाहूंगा, जहां Lombok जैसी चीज़ों से निपटना नहीं पड़ता। Java records अच्छे तो हैं

    • फिर भी Java programmers पुराने idioms का इस्तेमाल जारी रखेंगे, और पुराने codebases से बंधे रहेंगे। ऐसी चीज़ों को व्यापक रूप से इस्तेमाल होने में काफी समय लगेगा, इसलिए Kotlin programmers को चिंता करने की जरूरत नहीं
    • Java का दूसरे languages से inspiration लेने का इतिहास लंबा है। पिछली versions के साथ compatibility को आम तौर पर बनाए रखते हुए भी इसका काफी अच्छी pace से आगे बढ़ना देखना अच्छा है