1 पॉइंट द्वारा GN⁺ 5 일 전 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • selectors और rules के ज़रिए target set चुनकर properties लागू करने वाली CSS की संरचना, sets और rules पर काम करने वाले Datalog से रूपात्मक रूप से मिलती-जुलती है
  • div.awesome जैसे selector combination intersection बनाते हैं, और Datalog में एक ही variable को दोहराने के तरीके से वैसा ही join होता है
  • मौजूदा CSS computed style के परिणाम को फिर से selection condition के रूप में इस्तेमाल नहीं कर सकती, इसलिए recursive transitive queries या derived state के बार-बार propagation को सीधे व्यक्त करना कठिन है
  • Datalog recursive rules और fixpoint evaluation के ज़रिए relations को तब तक बढ़ाता है जब तक नए facts बनना बंद न हो जाएँ, और monotonicity की वजह से सीमित दायरे में computation समाप्त हो सकती है
  • वास्तविक CSS में Container Queries जैसी सुविधाओं से ancestor की जानकारी पढ़ी जा सकती है, लेकिन यह feedback loop और cycle को रोकने की दिशा चुनती है; इसके बावजूद CSS syntax को recursive queries से जोड़ने की गुंजाइश बनी हुई है

CSS और Datalog की मिलती-जुलती संरचना

  • CSS में target set selection और चुने गए targets पर rule application जैसी संरचना होती है
    • HTML elements जैसी "Things" पहले से मौजूद होती हैं, और selector समान गुणों वाले set की ओर इशारा करता है
    • div, #child, .awesome, [data-custom-attribute="foo"] जैसे selectors से set का वर्णन किया जा सकता है
    • div.awesome की तरह selectors को जोड़कर intersection बनाया जा सकता है
  • CSS rule selector और declaration को बाँधकर चुने गए elements पर color या font-size जैसी properties सेट करता है
    • लेकिन ऐसी properties आम तौर पर भाषा के बाहर की state बदलती हैं, और उसके परिणाम को फिर से selector condition नहीं बनाया जा सकता
    • div[color=red] की तरह style result को दोबारा query करने वाला रूप browser स्वीकार नहीं करता
  • Datalog भी इसी तरह facts के set और rule-based derivation से काम करता है
    • parent(alice, bob) जैसे atoms और relations इसकी मूल इकाइयाँ हैं
    • variables X, Y का उपयोग करके condition से मेल खाने वाले items का set चुना जा सकता है
    • एक ही variable को दोहराकर conditions जोड़ने पर CSS के selector combination जैसा join होता है
  • head(X, Y) :- body1(X, Z), body2(Z, Y) संरचना, दिशा उलटी होने के अलावा, CSS rule से मिलती-जुलती है
    • CSS का selector Datalog के body के अधिक करीब है, और declaration head के करीब है
    • div.awesome { color: red; } का समकक्ष color(X, red) :- div(X), class(X, awesome). है

recursive queries जो CSS नहीं कर पाती

  • data-theme="dark" के भीतर आने वाले सभी focused elements पर inverted style लागू करना, लेकिन बीच में data-theme="light" आ जाए तो रुक जाना, ऐसी condition के लिए transitive query चाहिए
    • वास्तविक CSS में [data-theme="dark"] :focus और [data-theme="dark"] [data-theme="light"] :focus जैसे rules से केवल कुछ हिस्सा संभाला जा सकता है
    • nesting level बढ़ने पर rules लगातार जोड़ने पड़ते हैं, और recursive relation को सीधे व्यक्त करना कठिन है
  • ज़रूरी condition यह है कि किसी element के effectively-dark होने का recursive निर्णय किया जाए
    • अगर वह स्वयं data-theme="dark" है, तो वह effectively-dark है
    • effectively-dark ancestor के नीचे आने वाला child भी, यदि बीच में data-theme="light" न हो, effectively-dark बन जाता है
    • इसी state के आधार पर .effectively-dark :focus पर style लागू होना चाहिए
  • काल्पनिक CSSLog syntax में rules class: +effectively-dark की तरह derived state जोड़ सकते हैं
    • .effectively-dark > :not([data-theme="light"]) child तक state propagate करता है
    • rules को target state तक पहुँचने तक recursively repeat होना पड़ेगा
  • इस तरह का recursive propagation मौजूदा CSS में व्यक्त करना कठिन है
    • लेख के अंत में कुछ मिलते-जुलते तरीके भी आते हैं, लेकिन वे उसी सिद्धांत का सामान्य समाधान नहीं हैं

Datalog में recursion और fixpoint

  • Datalog, मौजूदा facts से नए facts derive करने के तरीके से काम करता है और recursion को मूल रूप से संभालता है
    • ancestor(X, Y) :- parent(X, Y).
    • ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y).
  • ancestor rule parent relation के आधार पर ancestor relation को चरण-दर-चरण बढ़ाता है
    • parent(alice, bob) से पहले ancestor(alice, bob) बनता है
    • फिर alice -> bob -> carol, alice -> bob -> dave जैसे paths भी अतिरिक्त रूप से derive होते हैं
  • यह computation explicit for loop के बिना भी fixpoint evaluation से अंत तक चलती है
    • शुरुआत में केवल घोषित base facts का उपयोग होता है
    • सभी rules के body को मौजूदा facts set पर लागू करके head जोड़ा जाता है
    • जब नए facts बनना बंद हो जाते हैं, तब प्रक्रिया रुक जाती है
  • यह तरीका समाप्त हो जाता है क्योंकि इसमें monotonicity होती है
    • facts केवल जोड़े जाते हैं, हटाए नहीं जाते, इसलिए ज्ञात facts का set लगातार बड़ा ही होता है
    • यदि शुरुआत सीमित facts set से हो, तो derive किए जा सकने वाले facts की संख्या भी सीमित रहती है
    • इसके उलट, facts हटाए जा सकें तो पहले के निष्कर्ष पलट सकते हैं और system infinite loop में फँस सकता है

Container Queries और वास्तविक CSS की सीमाएँ

  • वास्तविक CSS की Container Queries ancestor या container के style के आधार पर rules लागू कर सकती हैं
    • यह @container style(--theme: dark) { .card { background: royalblue; color: white; } } जैसे रूप को support करती है
  • लेकिन transitive dark mode वाला उदाहरण, साधारण ancestor lookup से अधिक मजबूत condition माँगता है
    • हर element को यह जानना होगा कि वह स्वयं effectively-dark है या नहीं
    • यह state उसके पूरे descendants तक transitively propagate होनी चाहिए
    • data-theme="light" की boundary पर propagation रुक जाना चाहिए
  • Container Queries दूसरी condition को संभाल नहीं पातीं
    • ancestor की custom property पढ़ी जा सकती है, लेकिन किसी दूसरे rule द्वारा पहले से compute की गई derived state को फिर से query नहीं किया जा सकता
    • DOM में मूल रूप से मौजूद जानकारी देखी जा सकती है, लेकिन recursive computation के परिणाम को selector condition नहीं बनाया जा सकता
  • 2015 की संबंधित पोस्ट में भी बताया गया था कि element queries इसी समस्या से टकराईं
    • यदि query से set की गई property को फिर से query करने दिया जाए, तो loop और infinite repetition का जोखिम बढ़ जाता है
  • CSS Working Group ने इस समस्या से information flow की दिशा पर प्रतिबंध लगाकर बचाव किया है
    • descendants को ancestor की जानकारी query करने की अनुमति है
    • उलटी दिशा के feedback या अपनी ही style में cycle बनने से रोका जाता है
    • इसलिए fixpoint semantics के बिना भी computation सीमित रखी जा सकती है

CSS syntax को recursive query language में उलटने की संभावना

  • Datalog semantics को CSS में डालने के बजाय, CSS syntax को Datalog के ऊपर रखने की दिशा एक अधिक व्यावहारिक नए रास्ते के रूप में प्रस्तुत की गई है
    • Datalog का :-, पूर्ण विराम, declaration के बिना atoms जैसी syntax आधुनिक language users के लिए entry barrier ऊँची बनाती है
    • CSS में tree structure संभालने के लिए पहले से ही समृद्ध selector syntax मौजूद है
  • लेख यह भी बताता है कि वास्तविक डेटा में tree-shaped structures बहुत आम हैं
    • JSON
    • AST
    • filesystem
    • org chart
    • XML
  • ऐसे क्षेत्रों में parent/child relation को implicitly संभालने वाली CSS-शैली syntax और fixpoint recursion का संयोजन उपयोगी हो सकता है
    • सामान्य Datalog में tree structure को relational form में दोबारा लिखना पड़ता है, जो काफ़ी झंझट वाला है
    • अगर CSS selector की समझ को ज्यों का त्यों recursive queries में लाया जाए, तो अधिक programmers इसे आसानी से अपना सकते हैं
  • इस तरह का tool अभी स्पष्ट रूप से दिखाई नहीं देता
    • "CSSLog" नाम फिलहाल अस्थायी है, और भविष्य में बेहतर नाम वाली language आ सकती है
    • recursive tree queries को अधिक परिचित notation में संभालने की गुंजाइश अभी भी बनी हुई है

पूरक बिंदु और संदर्भ लिंक

  • Datalog 1970 के दशक से relational databases और उस समय के AI research के संदर्भ में उभरा था, और बाद में कई रूपों में बार-बार सामने आता रहा
  • fixpoint computation का एक सरल रूप naive evaluation के रूप में प्रस्तुत किया जाता है, लेकिन यह हर बार पहले से ज्ञात facts को फिर से compute करके अप्रभावी हो सकता है
    • हर चरण में केवल नए निकले facts का उपयोग करने वाला semi-naive evaluation एक प्रमुख सुधार दिशा के रूप में साथ में उल्लेखित है
  • monotonicity, distributed systems में भी उपयोगी गुण साबित होती है
  • custom property inheritance से transitive dark mode की आंशिक नकल करने का एक तरीका भी है
    • [data-theme="dark"] { --effective-theme: dark; }
    • [data-theme="light"] { --effective-theme: light; }
    • @container style(--effective-theme: dark) { :focus { outline-color: white; } }
    • यह तरीका इस खास मामले में अधिकांशतः काम करता है, लेकिन वास्तविक transitive closure को सामान्य रूप से उपलब्ध नहीं कराता

1 टिप्पणियां

 
GN⁺ 5 일 전
Hacker News टिप्पणियाँ
  • CSS selectors को XPath की तुलना में लिखना बहुत आसान है
    हाल ही में PHP के नए DOM API पर एक प्रस्तुति भी थी, जिसमें बताया गया कि अब HTML और CSS selectors को native तौर पर बहुत आसानी से हैंडल किया जा सकता है। पहले CSS को XPath में बदलना पड़ता था
    [1] https://speakerdeck.com/keyvan/parsing-html-with-php-8-dot-4...
    अफ़सोस है कि यह ब्राउज़र styling-केंद्रित तरीके से विकसित हुआ, इसलिए इसमें XPath जैसी text content आधारित selection जैसी सुविधाएँ नहीं हैं
    मुझे पता है कि पहले इसके प्रस्ताव आए थे, लेकिन ब्राउज़र rendering context में performance समस्याएँ आ सकती थीं, इसलिए शायद यह spec में शामिल नहीं हो पाया

    • LLM भी CSS selectors को काफ़ी अच्छी तरह संभाल लेते हैं
      एक document editing agent बनाते समय मैंने दस्तावेज़ को HTML के रूप में दिखाया और LLM से सिर्फ़ CSS selector निर्दिष्ट करवाकर ज़रूरी हिस्से context में खींचने दिए, और यह लगभग जादू की तरह काम करता था
    • client side पर querySelector/querySelectorAll इतने व्यापक रूप से इस्तेमाल होते हैं, इसलिए PHP के नए DOM में उनका आना अच्छा लगा
      लोग उसी परिचित तरीके से इसे इस्तेमाल कर सकते हैं
  • काश CSS grammar और CSSWG द्वारा परिभाषित rules, functions, units जैसी पूरी प्रणाली को अलग से बुलाने के लिए कोई नाम होता
    इसमें काफ़ी संभावना है, लेकिन दूसरे use cases पर बात करने या उन्हें खोजने के लिए आख़िरकार GitHub के उस कोड को खंगालना पड़ता है जिसमें CSS parser शामिल हो, ताकि पता चल सके कि लोग कैसी अजीब चीज़ें बना रहे हैं
    मैं कुछ वैसा भी छेड़छाड़ कर रहा हूँ जो किसी अजीब template engine जैसा है, जिसमें हल्की node-आधारित markup language, template में क्या जाएगा यह व्यक्त करने वाले CSS selectors, और इन टुकड़ों को कैसे जोड़ा जाए इसे नियंत्रित करने वाला CSS-जैसा grammar मिला हुआ है

    • मुझे लगता है कि standards में यह विभाजन पहले से काफ़ी साफ़ है
      https://www.w3.org/TR/selectors-3/
      DOM spec भी इसी का संदर्भ देती है
      https://dom.spec.whatwg.org/#selectors
      इसलिए CSS selector एक umbrella term के रूप में पहले से सही है, और इसे सिर्फ़ selector भी कहा जा सकता है
      DOM selector नाम और साफ़ लग सकता है, लेकिन अगर static CSS में इस्तेमाल होने वाले selectors या JS engine के बाहर के दूसरे DOM engines (XML parser, PHP DOM API आदि) के selectors को भी सोचें, तो यह उल्टा और भ्रमित कर सकता है
      साथ ही :hover या ::target-text जैसे ऐसे special selectors भी हैं जो सीधे ब्राउज़र rendering/navigation से जुड़े हैं
      फिर भी, ब्राउज़र या CSS से कम जुड़े हुए न्यूनतम query grammar subset के लिए अलग नाम होना उपयोगी हो सकता है
  • मुझे पुराने conference में देखा हुआ https://github.com/braposo/graphql-css याद आ गया
    यह मज़ाकिया project था, लेकिन यह अच्छी तरह दिखाता था कि patterns को दूसरे contexts में transplant करके दोबारा इस्तेमाल करने का तरीका अप्रत्याशित चीज़ों को संभव बना सकता है

    • यह मज़ेदार है
      मैं भी ठीक इसी तरह अलग contexts के patterns उठाकर आज़माने की कोशिश कर रहा हूँ
      ज़्यादातर चीज़ें शायद कहीं दूर तक न जाएँ, फिर भी hacker संवेदना के हिसाब से यह काफ़ी दिलचस्प है
  • pyastgrep में, जैसा कि https://pyastgrep.readthedocs.io/en/latest/ पर दिखता है, Python grammar को query करने के लिए CSS selectors इस्तेमाल किए जा सकते हैं
    default XPath है, और उदाहरण के लिए pyastgrep --css 'Call > func > Name#main' जैसा इस्तेमाल संभव है

    • यह वाकई बहुत अच्छा है
      यह लगभग ठीक उसी दिशा से मेल खाता है जिसकी ओर मैं इशारा करना चाहता था
  • मुझे ठीक से समझ नहीं आ रहा कि यह किस scenario को हल करता है
    अभी भी child के आधार पर parent को conditionally बदला जा सकता है। उदाहरण के लिए pre की default padding 16px है, और अगर उसका direct child code है तो &:has(> code) से इसे 0 किया जा सकता है

    • दरअसल यह शुरुआत में इस बात से निकला था कि दो अलग ideas एक-दूसरे से मिलते-जुलते लगते हैं, और फिर उस connection को कई दिशाओं में धकेलकर देखने जैसा बन गया
      निष्कर्ष भी "आधुनिक CSS की सीमाओं को ठीक करना चाहिए" से ज़्यादा इस बात के करीब है कि शायद CSS-जैसा grammar किसी Datalog-जैसी system पर चढ़ाने से tree-आकृति वाले data को संभालना ज़्यादा इंजीनियरों के लिए परिचित बनाया जा सकता है
    • यहाँ बात एक style calculation में हल होने वाली चीज़ की नहीं, बल्कि selector से match हुए target के underlying data को ही modify करने वाले grammar की है
      यानी बात DOM में नए child elements या attributes जोड़ने की है
  • मौजूदा LLM CSS को बहुत अच्छी तरह नहीं संभालते, इसलिए उल्टा यह आज़माकर देखना दिलचस्प होगा कि क्या इससे LLM ज़्यादा सरल तरीके से reason कर पाते हैं

  • इसका कोई वास्तविक उपयोग तुरंत नहीं सूझता, लेकिन यह फिर भी काफ़ी cool है

  • हम्म... कहीं यह बस JQ तो नहीं है?

  • मुझे CSS कुछ हद तक पसंद है, लेकिन इसकी बढ़ती हुई complexity creep पसंद नहीं
    मैं समझता हूँ कि programming languages, non-programming languages से ज़्यादा शक्तिशाली हो जाती हैं, लेकिन HTML, CSS और JavaScript को लगातार और जटिल बनाने की बजाय शायद बेहतर होता कि कुछ और आकर पूरे सेट को replace कर देता
    HTML5 के नए elements भी क्यों ज़रूरी हैं, यह मुझे ज़्यादातर समझ नहीं आता, इसलिए मैं उन्हें शायद ही कभी इस्तेमाल करता हूँ। आख़िर में कई containers बस unique ID लगे div ही लगते हैं, और कभी-कभी लगता था कि internal link के लिए href navigation हेतु उन IDs के aliases जैसे कुछ होने चाहिए थे
    [data-theme="dark"] [data-theme="light"] :focus { outline-color: black; } जैसी चीज़ को दिमाग़ में parse करने में बहुत समय लगता है, इसलिए यह अब elegant या simple नहीं लगती
    दूसरी तरफ़ h2 { color: red; } अब भी simple है
    ancestor(X, Y) :- parent(X, Y). जैसा expression देखते ही सोचने का मन नहीं करता। :- आख़िर है क्या, मुस्कुराते चेहरे जैसा दिखता है
    @container style(--theme: dark) { .card { background: royalblue; color: white; } } पर तो मैंने पढ़ना ही छोड़ दिया
    अजीब लगता है कि जो standard पहले ठीक काम करता था, वह समय के साथ बिगड़ता जा रहा है

    • मेरा इरादा CSS में और syntax व semantics जोड़ने का नहीं, बल्कि CSS से ideas चुराकर logic/relational query language के साथ उसकी समानता का उपयोग करते हुए कुछ नया बनाने के ज़्यादा करीब है
      उदाहरण के लिए [data-theme="dark"] [data-theme="light"] :focus { outline-color: black; } को अंग्रेज़ी जैसी pseudocode में खोलें तो इसका मतलब लगभग यह है कि अगर data-theme="dark" वाला X है, उसका child Y data-theme="light" है, और वह focus स्थिति में है, तो Y का outline-color black कर दो
      इसलिए इसे Datalog शैली में outline-color(Y, black) if data-theme(X, "dark") and parent(X, Y) and data-theme(Y, "light") and focused(Y) की तरह लिखा जा सकता है
      यानी :- को if से और comma को and से बदलने जैसा
      इससे आगे जाकर इसे Y.outline_color := black if X.data-theme == dark and Y.parent == X and Y.data-theme == dark and Y.focused की तरह भी लिखा जा सकता है, ताकि attr(X, val) को X.attr == val जैसी UFCS-जैसी syntax sugar की तरह दिखाया जा सके
      अगर इसे और ALGOL परिवार जैसा बनाना हो, तो forall Y { Y.outline_color := black if Y.data_theme == "dark" and Y.focused and Y.parent.data_theme == "light" } जैसा भी लिखा जा सकता है
      यहाँ Y को explicitly introduce किया गया है और एक join को implicit बना दिया गया है, जिससे यह ज़्यादा सामान्य programming जैसा दिखता है, लेकिन असल में Datalog engine dependencies बदलने पर हर बार ऐसे loop को efficiently चलाता है