- डेटा संरचनाओं में आइटम्स को comma से अलग करते समय trailing separator की अनुमति हो तो आइटम जोड़ना, हटाना और पुनर्व्यवस्थित करना एक ही तरह के text change के रूप में संभाला जा सकता है
- JSON आख़िरी member के बाद comma को मना करता है, इसलिए अंत में key जोड़ने या हटाने पर मौजूदा line भी बदलनी पड़ती है और यह एक विशेष मामला बन जाता है
- Haskell records, TLA+ variable declarations, और Prolog rules में भी separator की position या terminating symbol की वजह से पहली line और आख़िरी line के बदलाव अलग तरह से संभाले जाते हैं
- Python और Go trailing comma की अनुमति देते हैं, लेकिन leading comma की नहीं; जबकि Alloy leading और trailing comma दोनों की अनुमति देता है
- trailing separator control constructs में parsing ambiguity पैदा कर सकते हैं, और Python के single-element tuple की तरह data syntax में meaning अलग करने के लिए भी इस्तेमाल होते हैं
JSON में आख़िरी comma की समस्या
- JSON object में members के बीच comma की अनुमति है, लेकिन आख़िरी member के बाद आने वाला comma grammar के अनुसार मान्य नहीं है
{
"a": 1,
"b": 2,
"c": 3
}
- उसी object में
"c": 3, की तरह आख़िरी member के बाद comma लगा दें तो वह invalid JSON बन जाता है
{
"a": 1,
"b": 2,
"c": 3,
}
- अगर trailing comma की अनुमति हो, तो
"a" से पहले "x" जोड़ने और "c" के बाद "y" जोड़ने के लिए सिर्फ़ एक जैसे line additions की ज़रूरत होगी
{
+ "x": 0,
"a": 1,
"b": 2,
"c": 3,
+ "y": 4,
}
- मौजूदा JSON grammar में आख़िरी position पर key जोड़ते समय मौजूदा आख़िरी line
"c": 3 में भी comma जोड़ना पड़ता है, इसलिए बदलाव ज़्यादा जटिल हो जाता है
{
+ "x": 0,
"a": 1,
"b": 2,
- "c": 3
+ "c": 3,
+ "y": 4
}
- element हटाते समय भी सिर्फ़ वही line नहीं हटाई जा सकती; यह भी देखना पड़ता है कि आख़िरी line पर trailing comma न बच जाए
- अगर object का value खुद multi-line array या object हो, तो “trailing comma नहीं” की वजह से transformation और जटिल हो जाता है
दूसरी भाषाओं में मिलते-जुलते मामले
-
Haskell record
- Haskell record types में comma को हर line के आगे रखने वाली “partial bullet point” style इस्तेमाल कर सकता है
data Drone = Drone
{ xPos :: Int
, yPos :: Int
, zPos :: Int
}
- यह तरीका आख़िरी line बदलना आसान बनाता है, लेकिन पहली line बदलना और कठिन कर देता है
-
TLA+
- TLA+ में variable list और sequence में बिना end comma वाला रूप valid है
VARIABLES a, b, c
vars ==
- उसी syntax में आख़िरी item के बाद comma लगा दें तो वह invalid हो जाता है
VARIABLES a, b, c,
vars ==
- TLA+ specifications लिखते समय top-level variables लगातार जोड़ने पड़ सकते हैं, इसलिए यह सीमा असुविधाजनक है
- PlusCal DSL में यह समस्या नहीं है, और variable declarations को semicolon से सूचीबद्ध किया जा सकता है
(*--algorithm foo {
variables a; b; c;
-
Prolog
- Prolog जैसी logic languages trailing separator की अनुमति नहीं देतीं, और अलग terminating symbol भी इस्तेमाल करती हैं
foo(A, B, C) :-
A = 1, % comma
B = 2, % comma
C = 3. % period!
- आख़िरी period को अलग line पर रखकर उसे braces जैसा माना जा सकता है, लेकिन यह standard syntax नहीं है और trailing separator का लाभ भी नहीं मिलता
foo(A, B, C) :-
A = 1,
B = 2,
C = 3
.
बेहतर तरीका
-
trailing separator की अनुमति देने वाली भाषाएँ
- Go map literals में आख़िरी item के बाद comma की अनुमति देता है
valid := map[string]int{
"a": 1,
"b": 2,
"c": 3,
}
- Python भी dictionaries में आख़िरी item के बाद comma की अनुमति देता है
valid = {
"a": 1,
"b": 2,
"c": 3,
}
- Python और Go में comma पीछे आ सकता है, लेकिन आगे नहीं, इसलिए पूरी bullet point style नहीं बन पाती
invalid = {
, "a": 1
, "b": 2
, "c": 3
}
-
leading separator और Alloy
- TLA+ leading conjunction और leading disjunction operator की अनुमति देता है, लेकिन
(a &&) की तरह पीछे जोड़ने वाला रूप मान्य नहीं है
// Not TLA+ but the same semantics
|| && a == 1
&& b == 2
|| && a == 3
&& b == 4
- Alloy leading comma और trailing comma दोनों की अनुमति देता है
sig Valid {
, a: 1
, b: 2
}
sig AlsoValid {
a: 1,
b: 2,
}
- Alloy empty separator की भी अनुमति देता है, इसलिए सिर्फ़ कई commas वाली lines भी valid मानी जाती हैं
sig StillValid {
,, a: 1,,
,,,,,,,,,
,, b: 2,,
}
प्रतिवाद: parsing ambiguity
-
Prolog के control separator
- trailing separator के विरोध में एक तर्क यह है कि parsing अस्पष्ट हो सकती है
- Prolog में period से rule समाप्त करने पर यह साफ़ होता है कि
foo और bar अलग definitions हैं
foo(A, B) :-
A = 1,
B = 2.
bar(c).
- अगर rule terminator को comma से बदल दिया जाए, तो
bar(c) को foo definition का हिस्सा भी समझा जा सकता है
foo(A, B) :-
A = 1,
B = 2,
bar(c),
- इस स्थिति में
foo को तभी true माना जा सकता है जब bar(c) भी true हो
-
Ruby के method calls
- Ruby में line break के बाद भी method chain जारी रखी जा सकती है, और नीचे दिया गया code 5 print करता है
puts 3.
succ().
succ()
- अगर method call के बाद trailing separator की अनुमति हो, तो
quux() top-level function है या foo का method, यह स्पष्ट नहीं रहेगा
foo.
bar().
baz().
quux()
- Prolog और Ruby के उदाहरण data separator नहीं, बल्कि control separator से जुड़ी ambiguity हैं
data syntax में अपवाद: Python tuple
- Python parentheses का उपयोग expression grouping और tuple definition दोनों के लिए करता है
(2+3) expression evaluation के रूप में संभाला जाता है और int बनता है
>>> x = (2+3)
>>> type(x)
(2+3,) trailing comma की वजह से single-element tuple माना जाता है
>>> x = (2+3,)
>>> type(x)
- Python में यह मामला दिखाता है कि trailing data separator expression और single-element tuple में अंतर बताने का काम करता है
1 टिप्पणियां
Lobste.rs की रायें
JSON syntax ऑब्जेक्ट के दो members के बीच comma रखने देता है, लेकिन member के बाद trailing comma नहीं रख सकता। इसे “design mistake” कहना ठीक नहीं होगा। क्योंकि वह विकल्प मौजूद ही नहीं था JSON लगभग 2000~2001 के आसपास ECMAScript 3 के subset के रूप में बनाया गया था, और informational RFC 4627 2006 में लिखा गया। JavaScript का subset होने की वजह से ब्राउज़र में
evalसे सीधे चल जाना JSON का उद्देश्य भी था और उसकी सफलता की कुंजी भी, और ब्राउज़र का native JSON API 2009 में जाकर जोड़ा गया ES5 में trailing comma को specification में शामिल भी दिसंबर 2009 में किया गया, इसलिए trailing comma वाला JSON शुरू से ही अपने उद्देश्य के अनुरूप मौजूद नहीं हो सकता थाProlog में सबसे ज़्यादा होने वाली असुविधाओं में से एक यही है। Predicate पर काम करते समय आखिर में
,true.जोड़कर रखो, तो ऊपर की lines को rearrange या comment out करते वक्त आखिरी period की चिंता नहीं करनी पड़ती, इसलिए सुविधा होती हैwhere true / and ... / and ...वाले रूप में WHERE clause लिखती है। यहाँ slash का मतलब line break है इससे किसी भी condition को बिना special handling के आसानी से edit किया जा सकता हैZig trailing comma की अनुमति देता है, और इससे non-configurable formatter को control किया जा सकता है
.{1, 2, 3,}इस तरह बदल जाता हैऔर vertically formatted literal में trailing comma हटा देने का मतलब horizontal alignment माँगना होता है:
.{ 1, 2, 3 }यह इन पर भी काम करता है: container type definitionstruct { a: u32, b: u32, }, function signaturefn foo(a: u32, b: u32,) void {}, function callfoo(1, 2,);इन सब मामलों में trailing comma से auto-formatting को control किया जा सकता है यह feature इतना पसंद आया कि मैंने इसे अपने HTML language server/auto-formatter में भी जोड़ दिया Before:After:
https://github.com/kristoff-it/superhtml
100% सहमत। trailing separator के बिना कोई नई language हो तो मैं व्यक्तिगत रूप से उसके लिए थोड़ा negative point देता हूँ। इतना नहीं कि उस language की syntax से नफ़रत हो जाए, लेकिन यह छोटे घाव जैसी असुविधा है
foo(1,2,3,4,)?bar(,1,2,3,4)? अगरfoo()variable number of parameters की अनुमति देता है, तो क्या आखिरी argumentnilहै?bar()का पहला parameternilहै?Clojure और EDN में comma whitespace है। आम तौर पर same-line map literal में key-value pairs के बीच convention के तौर पर इस्तेमाल होता है, लेकिन पूरी तरह optional है
मुझे लगता है यहाँ tension बनने की बड़ी वजह यह है कि जब एक ही line में कई clauses हों, तो किसी न किसी तरह के separator की ज़रूरत पड़ती है
ऐसे मामलों में line-based diff से argument जोड़ना या हटाना हमेशा अटपटा लगता है। सिर्फ एक argument को comment out करते समय भी थोड़ी सावधानी और मेहनत चाहिए। बदले में आप एक line में कई items रखने की conciseness स्वीकार करते हैं लेकिन जब आप हर line में सिर्फ एक clause रखना शुरू करते हैं, तो आदर्श रूप से आप ज़्यादा साफ diff और simple line comments से आसानी से disable करने का तरीका चाहते हैं स्पष्ट जवाब है कि line break को standard separator की तरह treat किया जाए
बहुत-सी languages
;के साथ ऐसा करती हैं। अगर style ऐसा हो कि एक line में कई statements न लिखने की सलाह दी जाए, तो;लगभग दिखता ही नहीं। comma के साथ भी ऐसा करने वाली language मुझे नहीं पता, लेकिन hobby language में इसे आज़माने का मन हुआ था बेशक Lisp में subexpressions और subclauses हमेशा पूरी तरह अलग रहते हैं, इसलिए separator की ज़रूरत ही नहीं पड़ती और यह समस्या टल जाती हैLisp, या ज़्यादा सटीक रूप से S-expression, पसंद करने की मेरी एक वजह यही है। सोचने के लिए एक detail कम हो जाती है