Понастоящем декларативното програмиране е доминиращата парадигма на набор от разнообразни и обширни домейни като: бази данни, шаблони и управление на конфигурацията.
Накратко, декларативно програмиране се състои в казване на програма какво че трябва да направи вместо да каже как трябва ли да го направя. На практика този подход означава предоставяне на специфичен за даден език език (DSL). Специфичен за домейн език ) да изразя какво че потребителят иска и ги защитава от архитектури или конструкции на ниско ниво (контури, условни условия, задания), които материализират желаното крайно състояние.
Въпреки че тази парадигма е голямо подобрение спрямо императивния подход, който замени, бих могъл да кажа, че декларативното програмиране има значителни ограничения, ограничения, които смятам да проуча в тази статия. Освен това предлагам двустранен подход, който включва предимствата на декларативното програмиране, като същевременно преодолява неговите ограничения.
ПРОТЕСТ : Тази статия е написана в резултат на многогодишна лична борба с декларативни инструменти. Много от нещата, които той твърди, не са напълно доказани, а някои са представени като номинал. Правилната критика на декларативното програмиране ще отнеме много време, усилия и ще трябва да се върна назад и да използвам много от тези инструменти, но сърцето ми не би било в усилията. Смисълът на тази статия е да споделя мислите си с вас, без караница, просто ще покажа какво ми е подействало. Ако сте се борили с възходи и падения с декларативни инструменти за програмиране, тогава може да намерите малко удобство и някои алтернативи. И ако харесвате тази парадигма и нейните инструменти, не ми обръщайте много внимание.
Ако декларативното програмиране работи за вас Нямам право да ви казвам друго .
Преди да проуча ограниченията на декларативното програмиране, намирам за необходимо да разбера неговите достойнства.
Несъмнено най-ефективният инструмент за декларативно програмиране е релационната база данни (RDB). релационна база данни ). Може дори да се счита за първия декларативен инструмент. Както и да е, RDB има две свойства, които смятате за архетип на декларативното програмиране:
ръководство за стил на материален дизайн на google
Преди RDB, повечето системи за бази данни могат да бъдат достъпни чрез императивен код, който е невероятно зависим от подробности на ниско ниво, като записи, индекси и физически пътища до самите данни. Тъй като тези елементи се променят след определено време, кодът може да спре да работи поради някаква основна промяна в структурата на данните. Кодът, който ви остава в резултат, е труден за писане, отстраняване на грешки, четене и труден за поддръжка. Най-вероятно кодът беше много дълъг, пълен с много дълги условия, повторения и може би някои грешки, които не бяха забележими, но зависеха от държавата.
Ако случаят беше такъв, RDB осигуриха много голям скок на производителността за разработчиците на системи. Сега, вместо да имате хиляди редове императивен код, можете да имате дефинирана схема на данни и също стотици (или дори няколко) заявки. В резултат на това приложенията трябва да се справят само с абстрактно, смислено и трайно представяне на данните; и свързването му чрез мощен и в същото време прост език за заявки. RDB вероятно е помогнал за увеличаване на производителността на програмистите, както и на компаниите, които са ги наели, с порядък.
Какви са най-честите предимства на декларативното програмиране?
Въпреки че горните предимства са много често срещани, когато говорим за декларативно програмиране, ще ги обобщя в две качества, които ще служат като ръководни принципи при предлагането на алтернативен подход. Въпреки че горните са всички често цитирани предимства на декларативното програмиране, бих искал да ги събера в две качества, които ще служат като ръководни принципи, когато предлагам алтернативен подход.
В следващите два раздела ще обсъдя два от основните проблеми с декларативното програмиране: разделяне Y. липса на разполагане . Всеки преглед се нуждае от лош човек и затова ще използвам шаблон на HTML система като точен пример за недостатъците на декларативното програмиране.
Представете си, че трябва да напишете уеб приложение с нетривиален брой посещения. Кодирането на тези посещения в набор от HTML файлове не е опция, тъй като много компоненти на тези страници се променят.
Най-бързото решение, което би било да се генерира HTML чрез конкатенационни низове, всъщност ви изглежда ужасно и веднага ще започнете да търсите алтернатива. Стандартното решение е да се използва шаблонна система. Въпреки че съществуват различни видове системи с шаблони, засега ще оставим техните разлики настрана за тази дискусия. Можем да ги разглеждаме като подобни, когато става въпрос за шаблони системи, за да предоставят алтернатива на кодирането на кодиране на HTML низове, като се използват условни и цикли. Точно както RDB се появиха като алтернатива на обвързването чрез контури чрез записи на данни.
Да предположим, че избираме стандартната шаблонна система; В това ще намерите три източника на триене, които ще посоча във възходящ ред в зависимост от тяхното значение. Първото би било, че шаблонът трябва да се намира в отделен файл от вашия код. Тъй като системата с шаблони използва DSL, синтаксисът е различен и следователно не може да бъде в един и същ файл. В простите проекти, където броят на файловете не е голям, необходимостта файловете от шаблони да се държат отделно е два или три пъти по-важна, защото надвишава броя на файловете.
Ще направя изключение с шаблоните Вграден Ruby (ERB за съкращението на английски Вградени шаблони за Ruby ) защото тези те са вградени в изходния код на Ruby. Но това не важи за инструменти, вдъхновени от ERB, написани на други езици, тъй като тези шаблони трябва да бъдат запазени в различни файлове.
Вторият източник на триене е, че DSL има собствен синтаксис, различен от този на езика за програмиране. Ето защо модифицирането на DSL (и дори писането на собствен) е много по-трудно. За да преминете под въжето и да смените инструмента, ще трябва да научите за токенизацията и разбор (разбор), които се считат за интересни и предизвикателни, но много трудни. Според мен това е недостатък.
Може би се чудите: „Защо, по дяволите, бихте искали да модифицирате инструмента си? Ако правите стандартен проект, добре написаният инструмент трябва да работи за вас. ' Може би да, или може би не.
DSL никога не притежава цялата сила на езика за програмиране. Ако го направи, няма да е DSL, а пълен език за програмиране.
Но не е ли целта на DSL? Фактът на не имаме целия контрол на програмен език вече на разположение и по този начин можем да получим абстракция и също така да премахнем повече източници на бъгове . Може би. както и да е най-много на DSL започват просто и постепенно започват да включват голям брой възможности на програмен език, докато не станат Стани един . Шаблонните системи са идеалният пример. Сега нека разгледаме стандартните характеристики на шаблонните системи и как те се свързват с възможностите на език за програмиране:
Когато казват, че DSL е ограничен, тъй като едновременно е алчност и отхвърля контрола върху езика за програмиране, това е пряко пропорционално на факта, че DSL функциите са съпоставими с характеристиките на програмен език . Що се отнася до SQL, това не е така, защото повечето от нещата, които SQL предлага, не изглеждат като това, което обикновено бихте намерили в език за програмиране. И в другия край на спектъра откриваме шаблони системи, при които на практика всяка функция кара DSL да се сближава ОСНОВЕН .
Сега нека се върнем назад и да разгледаме тези три източника на триене par excellence, които са обобщени в концепцията за разделяне . Тъй като е отделен, DSL трябва да се намира в отделен файл; Трудно е да се модифицира (и още по-трудно да се напише) и (понякога, но не винаги) трябва да добавяте един по един, характеристиките, от които се нуждаете от истински език за програмиране.
Разделянето е присъщ проблем на всеки DSL, независимо колко добре е проектиран.
И сега втори проблем с декларативните инструменти, който е общ, но не присъщ.
Ако бях написал статия преди няколко месеца, този раздел щеше да бъде извикан Повечето декларативни инструменти са # @! $ # @! Комплекс, но не знам защо . В процеса на писане на тази статия намерих по-добър начин да го изразя: Повечето декларативни инструменти са по-сложни, отколкото би трябвало . В този раздел ще обясня защо. За да анализирам сложността на даден инструмент, обичам да предлагам мярка, наречена граница на сложност . Границата на сложност е разликата между решаването на даден проблем с инструмент или решаването му на ниско ниво (уж прост императивен код), което инструментът се опитва да замени. Когато предишното решение е по-сложно от следващото, тогава се оказваме с граница на сложност. Когато кажа по-сложни Имам предвид повече редове код, код, който е по-труден за четене, модифициране и поддържане, но не непременно всички едновременно.
Имайте предвид, че не сравняваме решението на ниско ниво с най-добрия инструмент, а сравняваме с нито един инструмент. Това наподобява медицинския принцип на „Първо, няма да навредиш“ .
Признаци на инструмент с голяма граница на сложност:
Може би се увличам от емоции, защото шаблонните системи не са така сложно, но тази относително малка степен на сложност не е достойна за неговия дизайн, вместо това областта на приложимостта е съвсем проста (не забравяйте, че ние генерираме HTML). Всеки път, когато същият подход се използва за по-сложен домейн (като управление на конфигурацията), границата на сложност може бързо да превърне вашия проект в тресавище.
Сега има изключения, при които не е напълно неприемливо инструментът да е малко по-сложен от ниското ниво, което искате да замените; ако инструментът дава код, който е по-четлив, кратък и правилен, може да си заслужава. Проблем е, когато инструментът е няколко пъти по-сложен от проблема, който замества, това е напълно неприемливо. Брайън Керниган е известен с това, че заяви: „ Това контролиране на сложността е същността на компютърното програмиране. ”Ако един инструмент само добавя значителна сложност към вашия проект, защо да го използвате?
И се чудим, защо някои декларативни инструменти са по-сложни от необходимото? Мисля, че би било грешка да се обвинява лош дизайн. Това е толкова общо обяснение, такъв ad-hominem аргумент към авторите на тези инструменти, че не е справедливо. Трябва да има друго обяснение, по-точно и информирано.
Моят аргумент е, че всеки инструмент, който предлага интерфейс от високо ниво за извличане на сура от ниско ниво да се разположи това по-високо ниво отдолу. Концепцията за да се разположи е включен в произведението на Кристофър Александър: 'Природата на реда' или Естеството на реда - по-специално том II. (Безнадеждно) е извън обхвата на тази статия (да не говорим, разбирам) да обобщим последиците от тази монументална работа върху софтуерния дизайн; въпреки че вярвам, че през следващите години въздействието му ще бъде огромно. Също така е извън обхвата на тази статия, за да предостави подробна дефиниция на процесите на внедряване. Но ще използвам концепцията за евристична форма .
Процесът на внедряване се състои от създаване на структура, без да се отрича съществуващата. На всяка стъпка всяка промяна (или диференциация, за да цитираме термините на Александър) остава в хармония с всяка предишна структура, когато предишната структура е просто кристализирана последователност от минали промени.
Най-интересното е това Unix това е отличен пример за разгръщане на по-високо ниво от едно отдолу. В Unix две сложни функции на операционната система, групови задачи и съпрограми (конвейри) са просто разширения на основните команди. За определени дизайнерски решения, като например превръщането на всичко в байтов поток, Shell a потребителска програма и I / O файловете са стандартни , Unix е в състояние да предостави тези функции толкова сложни, колкото и най-малко сложните. За да стресна защо тези примери за показване са отлични, ще цитирам някои откъси от Статия от 1979 г. От Денис Ричи, един от авторите на Unix:
Относно партидните задачи :
... Новата схема на процеса незабавно направи някои ценни функции тривиални за изпълнение; например отделен процес (с
&
) и рекурсивно използване на черупката като команда. Повечето системи трябва да доставят някакъв видaplicación de trabajo en lote
специална команда за интерпретатор и способност за файлове, различни от тези, използвани интерактивно.
Относно съвместните процедури :
Най-страхотното при тръбите на Unix е именно, че те са изградени от едни и същи постоянно използвани команди по прост начин.
Струва ми се, че тази елегантност и простота идва от процес на разполагане . Пакетните задачи и съпрограми се разполагат от предишни структури (командите работят в потребителска обвивка). Мисля, че поради минималистичната философия и ограничените ресурси на екипа, създал Unix, системата еволюира странично и като такава успя да включи разширени функции, без да разглежда отново най-основните, защото нямаше достатъчно ресурси за правене по друг начин.
При липса на процес на внедряване, високото ниво ще бъде значително по-сложно от необходимото. Тоест, сложността на повечето декларативни инструменти се развива от факта, че тяхното най-високо ниво не се разпростира от ниското ниво, което се опитват да заменят.
Тази липса на разгъване Ако пропуснете неологизма, това обикновено е оправдано от необходимостта да се защити потребителят от ниското ниво. Този акцент върху използването на poka-yoke (защита на потребителя от грешки на ниско ниво) идва на цена, което е граница на сложност, която се самоунищожава, защото допълнителната сложност ще генерира нови видове грешки. И за завършване, този вид грешки нямат нищо общо с проблема с домейна, а със самия инструмент. Няма да има преувеличение, ако опишем тези грешки като a ятрогения .
Инструментите за декларативни шаблони, когато се прилагат към задачата за генериране на HTML изгледи, са архетипен случай на високо ниво, отхвърлящо ниско ниво, което се претендира да отхвърли. Както добре? Защо генерирането на някакъв нетривиален изглед изисква логика и системите за шаблони, особено по-малко логичните, изцяло заобикалят логиката и след това се опитват да я изтласкат малко, когато смятат, че никой не гледа.
Забележка: още по-лоша обосновка за марж на сложност е, когато се опитват да продадат инструмент като магически или нещо, което просто върши работа ; ниското ниво на неяснота би трябвало да е нещо добро, защото магически инструмент винаги работи, без да знаете защо и как работи. Според моя опит, колкото по-вълшебен инструмент изглежда, толкова по-бързо ще превърне ентусиазма във разочарование.
А какво да кажем за разделянето на проблемите? Не трябва ли зрението и логиката да останат отделни? Най-честата грешка при това е да се приеме, че бизнес логиката и логиката на представяне са еднакви. Бизнес логиката няма никакъв смисъл в шаблона, но логиката на представяне съществува независимо от всичко. Заобикаляйки логиката на шаблона, логиката на презентацията се оставя настрана и я поставя на сървъра, където трябва да е тромаво, за да се адаптира. Всичко това ясно обяснение дължа на Алексей Боронин, който представя много добър случай в тази статия .
По мое мнение повече или по-малко две трети от работата на шаблона е в неговата логика на представяне, докато другата трета отговаря за справяне с общи проблеми като: конкатенационни низове, затворени тагове, специални избягващи символи и списъкът продължава. Това е двустранният характер на генерирането на HTML изгледи. Системите на отбора се грижат за второто полувреме, но не се справят много добре с първото полувреме. Шаблоните без логика обръщат гръб на този тип проблеми без колебание, принуждавайки ви да решите проблема. Други системи с шаблони страдат, защото всъщност трябва да предоставят нетривиален език за програмиране, така че техните потребители да могат да пишат логика на презентацията.
И накрая, декларативните инструменти за шаблони страдат от следното:
Ще завърша този преглед с аргумент, който логически е откъснат от нишката, която носи тази статия, но който е много близо до сантименталното ядро: Имаме малко време за учене. Животът е кратък и освен това трябва да работим. Когато отговаряме на ограниченията си, трябва да отделяме време за изучаване на нови неща, които ще бъдат полезни и ще обслужват времето ни, дори когато сме изправени пред бърза технологична промяна. Ето защо ви съветвам да използвате инструменти, които не само предоставят решение, но всъщност обслужват домейна, за който се отнася. RDB ви учи за данни, а Unix ви учи за концепции на операционната система, но с незадоволителни инструменти, които не се разгръщат, винаги съм се чувствал така, сякаш съм научил тънкостите на неоптимално решение, докато съм в тъмното за същността на проблем за решаване.
Евристиката, която предлагам да разгледате сега, е ** инструменти за стойност, които осветяват проблема с вашия домейн, а не инструменти, които скриват проблема ви с домейна зад самонадеяните функции **.
За да преодолея двата декларативни проблема с програмирането, които споменах тук, предлагам двойствен подход:
DSL структурата на данните (dsDSL) е DSL, която е изгражда със структурите на данни на програмен език . Основната идея е да използвате основни структури от данни, които имате на разположение, като низове, числа, масиви, обекти и функции и след това да комбинирате всичко, за да създадете извлечения, които могат да разрешат конкретен домейн.
Искаме да запазим контрол над декларираните (високо ниво) структури или действия, без да се налага да посочваме моделите, които тези конструкции (ниско ниво) прилагат. Искаме да преодолеем разликата между DSL и нашия език за програмиране, за да можем свободно да използваме пълната мощ на програмен език, когато имаме нужда от него. Това е не само възможно, но и директен подход чрез dsDSL.
инструменти за анализ на данни и визуализация
Ако бяхте попитали преди година, щях да мисля, че концепцията за dsDSL е идеалистична и тогава осъзнах, че JSON всъщност е чудесен пример за този подход! Анализираният JSON обект се състои от структури от данни, които декларативно представят входовете на данни, за да се възползват от DSL и също така да улеснят анализирането и обработката от език за програмиране. (Може да има други dsDSL в света, но дори не съм виждал такива. Ако знаете такива, бих се радвал да ги споделите в раздела за коментари.)
Подобно на JSON, dsDSL има следните атрибути:
parse
и stringify
.Но dsDSL надхвърля JSON в много отношения. Нека създадем dsDSL за генериране на HTML с помощта на Javascript. Тогава ще говоря за проблема дали този подход може да бъде разширен и на други езици (SPOILER: определено може да се направи в Ruby и Python, но вероятно не и в C).
HTML е език за маркиране, съставен от etiquetas
разграничени от ъглови скоби (<
и >
). Тези маркери могат да бъдат незадължително съдържание и атрибути. Атрибутите са просто списък с ключови / прогнозни атрибути и съдържанието може да бъде текст или други тагове. Атрибутите и съдържанието не са задължителни за всеки етикет. Опростявам всичко това, но е доста добре.
Много лесен начин за представяне на HTML таг в dsDSL е използването на масив с три елемента: - Tag: низ. - Атрибути: обект (от тип ключ / проста оценка) или sin definir
(ако не се изискват атрибути). - Съдържание: низ (текст), масив (друг етикет) или sin definir
(ако няма съдържание).
Например, Index
може да се запише като ['a', {href: 'views'}, 'Index']
.
Ако искаме да поставим този котва елемент в div
с links
на клас, можем да напишем: ['div', {class: 'links'}, ['a', {href: 'views'}, 'Index']]
.
За да изброим няколко html тагове на едно и също ниво, бихме могли да ги поставим в масив:
[ ['h1', '!Hola!'], ['a', {href: 'views'}, 'Index'] ]
Същият принцип може да се приложи за създаване на множество маркери в рамките на един етикет:
['body', [ ['h1', '!Hola!'], ['a', {href: 'views'}, 'Index'] ]]
Разбира се, този dsDSL няма да ни бъде от голяма полза, ако не генерираме HTML от него. Нуждаем се от функция generar
което ще накара dsDSL да създаде низ с HTML. Ето защо, ако изпълним generar (['a', {href: 'views'}, 'Index'])
, ще получим низа Index
.
Идеята на всеки DSL е да посочи някои конструкции с определена структура, които след това да бъдат предадени на функция. В този случай структурата, която прави dsDSL, е този масив, който има един до три елемента; тези матрици имат специфична структура. Ако generar
за да проверите вашите данни (и е важно и също така е лесно да ги потвърдите напълно, тъй като тези правила за валидиране са точната аналогия на синтаксиса на DSL), това ще ви каже точно къде сте сгрешили въведеното. След известно време ще започнете да разпознавате какво прави структурата валидна в dsDSL и тази структура ще бъде силно подсказваща за генерираното, което е в основата на нея.
Какви са достойнствата на dsDSL за разлика от DSL?
Сега имаме последния аргумент, който е доста силен, поради което ще го подчертая в този раздел. Какво имам предвид под правилно наети ? За да видим как работи това, нека разгледаме пример, в който искаме да изградим таблица за показване на информацията от масив, наречен Сега, последното твърдение е силно, така че ще прекарам останалата част от този раздел DATA
.
var DATA = [ {id: 1, descripción: 'Producto 1', precio: 20, onSale: verdadero, categorías: ['a']}, {id: 2, descripción: 'Producto 2', precio: 60, onSale: falso, categorías: ['b']}, {id: 3, descripción: 'Producto 3', precio: 120, onSale: falso, categorías: ['a', 'c']}, {id: 4, descripción: 'Producto 4', precio: 45, onSale: verdadero, categorías: ['a', 'b']} ]
В реално приложение DATA
той ще бъде генериран динамично от заявка за база данни.
Имаме и променлива FILTER
че при инициализация ще бъде масив с категории, които искаме да покажем.
Искаме нашата маса да направи това:
id
но го добавете като атрибут id
за всеки ред. АЛТЕРНАТИВНА ВЕРСИЯ: Добавете атрибут id
за всеки елемент tr
.onSale
ако продуктът се продава ( продава се ).FILTER
е празен масив, ще покажем всички продукти. И ако не, ще покажем продуктите, където продуктовата категория се съдържа в FILTER
.Можем да създадем логическата презентация, която отговаря на това изискване, в ~ 20 реда код:
function drawTable (DATA, FILTER) { var printableFields = ['description', 'price', 'categories']; DATA.sort (function (a, b) {return a.price - b.price}); return ['table', [ ['tr', dale.do (printableFields, function (field) { return ['th', field]; })], dale.do (DATA, function (product) { var matches = (! FILTER || FILTER.length === 0) || dale.stop (product.categories, true, function (category) { return FILTER.indexOf (category) !== -1; }); return matches === false ? [] : ['tr', { id: product.id, class: product.onSale ? 'onsale' : undefined }, dale.do (printableFields, function (field) { return ['td', product [field]]; })]; }) ]]; }
Наясно съм, че това не е ясен пример, но представлява доста опростен изглед на 4-те основни постоянни функции за съхранение, известни също като ЖЕСТКО . Всяко нетривиално уеб приложение ще има изгледи, които са по-сложни от това.
Сега нека видим какво прави този код. Първо дефинирайте функция, drawTable
да съдържа логическото представяне на чертежа на продуктовата таблица. Тази функция получава DATA
и FILTER
като параметри, затова може да се използва от различни набори от данни и филтри. drawTable
Той има двойна роля, като е частичен и помощник.
var drawTable = function (DATA, FILTER) {
Вътрешната променлива, printableFields
, е единственото място, където трябва да посочите кои полета да се отпечатват, за да се избегнат повторения и несъответствия, когато се сблъскате с променящи се изисквания.
var printableFields = ['description', 'price', 'categories'];
След това класифицираме DATA
според цената на техните продукти. Имайте предвид, че някои различни и по-сложни критерии за класификация биха били по-лесни за изпълнение, тъй като разполагаме с целия програмен език.
DATA.sort (function (a, b) {return a.price - b.price});
Тук връщаме буквален обект; масив, който съдържа „таблица“ като първи елемент и съдържанието му като втори. Това е dsDSL представяне на Сега създаваме ред със заглавията на таблицата. За да създадем вашето съдържание, ние използваме продължавай което е функция като Array.map , но това работи и за обекти. Нека да повторим Можете да видите, че току-що внедрихме итерация, работната сила за генериране на HTML и не беше необходимо да имаме DLS компилации; просто се нуждаем от функция за итерация през структура на данни и връщане на dsDSL. Подобна естествена или внедрена от потребителя функция би имала същия ефект. Сега трябва да прегледате продуктите, съдържащи се в Проверяваме дали този продукт е отхвърлен от Обърнете внимание на сложността на условния; той е съобразен с нашите изисквания и имаме пълната свобода да го изразим, защото сме на език за програмиране, а не на DSL. Ако Разбира се, ние затваряме всичко, което отваряме. Синтаксисът е забавен, нали? Сега, как да включим тази таблица в по-широк контекст? Пишем функция, наречена Ако не ви харесва начина, по който изглежда горният код, нищо, което казвам, няма да ви убеди. Това е dsDSL в най-добрия случай . На този етап можете да спрете да четете статията (и също така да оставите лош коментар, защото сте спечелили правото да го направите, ако сте стигнали до този момент!). Но сериозно, ако горният код не изглежда изискан, нищо в тази статия няма. За тези, които все още ме следват, бих искал да се върна към началното изявление на този раздел, което е какво dsDSL има предимствата както на високо, така и на ниско ниво : The предимство на ниско ниво става въпрос за писане на кодове, когато пожелаем, да се отървем от DSL тесната риза. The предимство на високо ниво е да използваме литерали, които представляват това, което искаме да декларираме, и да оставим функциите на инструмента да го преобразуват в желаното крайно състояние (в този случай последователност с HTML). Но как това се различава от чисто императивния код? Мисля, че елегантността на подхода dsDSL се свежда до факта, че код, написан по този начин, се състои предимно от изрази, а не от декларации . За да бъдем по-точни, всеки код, който използва dsDSL, на практика се състои от: Кодът, състоящ се предимно от изрази, който капсулира повечето декларации във функциите, е изключително кратък, тъй като всички повтарящи се модели могат лесно да бъдат абстрахирани. Можете да пишете произволен код, стига този код да връща литерал, който отговаря на много специфична непроизволна форма. Друга характеристика на dsDSL (която засега не може да бъде изследвана много) е възможността да се използват типове, за да се увеличи привлекателността и сочността на литералните структури. В следваща статия ще подчертая този въпрос. Може ли да се създаде dsDSL без JavaScript, Единственият истински език? Да, мисля, че е възможно, стига езикът да поддържа: Мисля, че това означава, че dsDSL са устойчиви в съвременния динамичен език (т.е.: Ruby, Python, Perl, PHP), но вероятно не в C или Java. В този раздел ще се опитам да покажа начин за разполагане на инструмент от най-високо ниво от вашия домейн. Накратко, подходът се състои от тези стъпки Какво по дяволите са модели на изобразяване Y. модели на генериране ? Добре, че попитахте. Представителните модели са моделите, при които трябва да можете да изразите проблем, който принадлежи на домейн, който се отнася до вашия инструмент. Това е азбука от структури, която ви позволява да напишете всеки шаблон, който искате да изразите в приложимия домейн. На DSL това биха били производствените правила. Сега да се върнем към нашия dsDSL, за да генерираме HTML. Моделите на изобразяване за HTML са както следва: Тези екземпляри могат да бъдат представени с dsDSL нотация, която определихме в предишния раздел. И това е всичко, от което се нуждаете, за да изобразите каквото и да е HTML, от което се нуждаете. По-сложни модели, като например условна итерация през обект за генериране на таблица, могат да бъдат внедрени с функции, които връщат споменатите по-горе модели на изобразяване и тези шаблони се преобразуват директно в HTML тагове. Ако моделите на изобразяване са структурите, използвани за изразяване на това, което искате, моделите на изобразяване са структурите, които вашият инструмент ще използва, за да преобразува образците на изобразяване в структури на ниско ниво. За HTML това са: Вярвате или не, това са моделите, които трябва да създадете, за да покажете dsDSL слой, който генерира HTML: Подобни модели могат да бъдат намерени за генериране на CSS. Всъщност лит прави и двете в ~ 250 реда код. И накрая, какво имам предвид под ' разходка, след това плъзнете ? Когато попаднем на проблем с домейн, искаме да използваме инструмент, който ни спасява от ужасните подробности за този домейн. С други думи, ние искаме да се отървем от ниското ниво, без никой да забележи, и колкото по-бързо, толкова по-добре. Подходът „ходи, после се плъзгай“ предлага точно обратното на това: останете по-дълго на ниското ниво. Приятете се към техните странности и разберете кои са от съществено значение и кои могат да бъдат избегнати, когато възникнат реални, разнообразни и полезни проблеми. След като вървите известно време на ниско ниво и след това решавате полезни проблеми, тогава ще имате по-дълбоко разбиране за неговото майсторство. Моделите на представяне и генериране ще се появят естествено; Те произтичат от естеството на проблемите, които се опитват да решат. След това можете да напишете кода, който ще ги използва. Ако работят, ще можете да се плъзнете през проблемите, където наскоро ви се е налагало да ходите. Плъзгането означава много неща; това предполага скорост, прецизност и липса на триене. Може би това качество се усеща повече, когато решавате проблеми с този инструмент, чувствате ли, че навлизате в проблема, или смятате, че се изплъзвате през него? Може би най-важното при инструмента за внедряване не е фактът, че ни освобождава от необходимостта да се справяме с ниския клас. По-скоро, като улавя емпирични модели на повторение на ниско ниво, един добър инструмент на високо ниво ни позволява да разберем напълно приложимостта на домейна. Инструментът за внедряване не само ще реши проблем - той също така ще ви помогне да разберете структурата на проблема. Така че не бягайте от ценен проблем. Първо преминете през него и след това се плъзнете през него. искаме да творим.
return ['table', [
printableFields
и генерирайте заглавки на таблици за всеки от тях: ['tr', dale.do (printableFields, function (field) { return ['th', field]; })],
DATA
. dale.do (DATA, function (product) {
FILTER
. Ако FILTER
е празно, можем да отпечатаме продукта. Ако FILTER
не е празно, ще прегледаме категориите продукти, докато намерим такъв, който е вътре FILTER
Правим това, използвайки dale.stop . var matches = (! FILTER || FILTER.length === 0) || dale.stop (product.categories, true, function (category) { return FILTER.indexOf (category) !== -1; });
coincidencia
е falsa
, връща се празен масив (така че не отпечатваме този продукт). В противен случай a със съответния идентификатор и клас, точно както итерираме през printableFields
за да отпечатате полетата несъмнено. return matches === false ? [] : ['tr', { id: product.id, class: product.onSale ? 'onsale' : undefined }, dale.do (printableFields, function (field) { return ['td', product [field]];
})]; }) ]]; }
drawAll
което ще извика всички функции, които генерират изгледите. Освен drawTable
, бихме могли да имаме и drawHeader
, drawFooter
и други сравними функции, всички тези dsDSL ще се върнат .var drawAll = function () { return generate ([ drawHeader (), drawTable (DATA, FILTER), drawFooter () ]); }
Първо вървете, после се плъзгайте: Как да разгърнем високото от ниското
['TAG']
['TAG', {attribute1: value1, attribute2: value2, ...}]
['TAG', 'CONTENTS']
['TAG', {attribute1: value1, ...}, 'CONTENTS']
['TAG1', ['TAG2', ...]]
[['TAG1', ...], ['TAG2', ...]]
condition ? ['TAG', ...] : []
/ В зависимост от определено условие, поставете атрибут или не: ['TAG', {class: condition ? 'someClass': undefined}, ...]
за какво е добър ruby on rails