jakubův notes – programování a vejšplechty

Píšu o:

phpeg – PEG pro PHP

Napsal jsem pacc a doufal jsem, že co se týče parsování něčeho v PHP, mám vystaráno, že mě už nic nezaskočí. Jak jsem se mýlil!

pacc (stejně jako mnoho dalších nástrojů) pracuje s bezkontextovými gramatikami (dále jen CFG) a používá „dvouúrovňové“ zpracování vstupu:

vstupní text → lexer → parser → výstup

Kdy se ale podobné zpracování nehodí? Např. pokud chcete zpracovat tenhle šablonovací jazyk. Lexer by se musel dost zkomplikovat a hodně by kopíroval procesy dějící se v parseru. Tudy cesta nevede.

A je tu phpeg. phpeg namísto CFG pracuje s PEGy. Stejně jako CFG jsou PEGy druhem formální gramatiky. Hlavním rozdílem je to, že operátor alternativy (u CFG |, u PEGů /) je u PEGů asymetrický, takže pokud uspěje první alternativa, druhá už není vyhodnocována. Jinak si syntaxe bere hodně z regulárních výrazů. Přidávají se ještě dva prefixové operátory – & a ! –, které uspějí tehdy, uspěje-li jimi prefixovaný výraz, ovšem tyto operátory nespořádají žádný vstupní text (resp. pokud vše uspěje kurzor v textu se vrátí na místo, kde by před tím, než-li byl výraz testován).

Doporučuji k pročtení Parsing Expression Grammars: A Recognition-Based Syntactic Foundation (PDF) a Experimenting with Programming Languages (PDF 1,34MB).

Jak začít používat phpeg? Nejdříve si budete muset stáhnout a nainstalovat pacc, proto phpeg ho používá bootstrappování (je sranda snažit se popsat gramatiku PEGu pomocí CFG, protože CFG nemá ! operátor; nepřišel jsem na to jak, takže bootstrapová verze PEG gramatiky phpegu musí mít mezi pravidly oddělovače). Až budete mít pacc nainstalovaný v $PATH, přejděte do adresáře se zdrojovými kódy a spusťte:

$ ./scripts/bootstrap.sh

Jestliže všechno proběhlo v pořádku, měl by se v lib/parse/ nacházet soubor php.php. Nyní můžete phpeg zkompilovat do jednoho spustitelného souboru a umístit třeba do /usr/bin/:

# ./scripts/compile.php /usr/bin/phpeg

Stejně jako u pacc, příklad kalkulačky:

exp  = e:exp "+" f:frac  -> $e + $f
     / e:exp "-" f:frac  -> $e - $f
     / f:frac            -> $f

frac = f:frac "*" n:num  -> $f * $n
     / f:frac "/" n:num  -> $f / $n
     / s "(" e:exp ")" s -> $e
     / n:num             -> $n

num  = s d:[0-9]+ s      -> intval($d)

s    = [ \t\r\n]*

PEG je sada pravidel, jak číst vstupní řetězec. Pravidla phpegu začínají nějakým identifikátorem, poté následuje rovnítko a pak samotné pravidlo. K jednolivým částem pravidla se nepřistupuje jako u pacc pomocí pořadového číslo (což by u těch složitějších šlo těžko), nýbrž se musí každá část, se kterou chce člověk pracovat, pojmenovat. To se dělá tak, že se dá identifikátor a dvojtečka před výraz, jehož hodnotu potřebujeme. Za šipkou následuje sémantická hodnota výrazu. Buď se může jednat o jednoduchý výraz, a ten se bere od začátku šipky do konce řádku, nebo o blok (ten se musí obklopit složenými závorkami). Při použití bloku je toto ekvivalentní zápis pravidla num výše:

num = s d:[0-9]+ s -> { return intval($d); }

Příklad se nachází ve zdrojové distribuci v adresáři examples/. Zkompilovat a vyzkoušet ho můžete následovně; přejděte do adresáře se zdrovými kódy a spusťte:

$ ./bin/phpeg -i ./examples/calculator.php.peg -fo ./calculator.php
$ cat >> ./calculator.php
list($ok, $output) = calculator(file_get_contents('php://stdin'));
echo "$output\n";
$ echo "22 * 2 - 2" | php -f ./calculator.php
42

Ve stylu convention over configuration, phpeg vyzkouší, jestli název vstupního, popř. výstupního souboru odpovídá jistému vzoru. Tím vzorem je název parsovací funkce následovaný tečkou, názvem parseru/generátoru, tečkou a příponou peg. Název parsovací funkce můžete též určit pomocí přepínače -p a typ parseru/generátoru zase přepínačem -t (více v nápovědě, -h). Např. pro soubor calculator.php.peg bude název parsovací funkce calculator a bude použit php parser/generátor (v současné době je dostupný pouze php parser/generátor; ale architektura phpeg je postavena tak, že se další dají snadno přidat).

Jestliže nechcete umisťovat funkci do globálního prostoru jmen, použijte a přepínač -n, a phpeg umístí na začátek souboru deklaraci namespace.

phpeg vygeneruje PHP skript s jedinou funkcí s dvěma parametry – řetězcem k parsování a polem možností. Tato funkce inicializuje instanci parsovací třídy řetězcem k parsování a jednotlivé klíče z pole možností namapuje na vlastnosti instance třídy (takže v sémantických akcích můžete používat $this->moje_predana_moznost apod.). Funkce vrací trojici – booleovskou hodnotu značící, jestli vše proběhlo v pořádku; sémantickou hodnotu startovacího pravidla; a pozici, kam se až parser dostal (v případě chyby pozici, kde parsování skončilo chybou).

Nutno přiznat phpeg je pomalejší než pacc. Při bootstrapu se nejdříve vygeneruje ze souboru lib/parse/bootstrap.y pomocí pacc LR parser a uloží se do souboru lib/parse/bootstrap.php. Ten potom přechroustá lib/parse/parse_php.bootstrap.peg a vyplivne soubor lib/parse/php.php. Do třetice všeho dobrého phpeg přegeneruje lib/parse/php.php ze souboru lib/parse/parse_php.php.peg pomocí php parseru/generátoru vygenerovaného v minulé fázi. bootstrap parser/generátor vygenerovaný paccem je asi 5krát rychlejší než php parser/generátor vygenerovaný v poslední fázi. Je tím vykoupeno to, že phpeg, jelikož pracuje s proudem znaků, nikoli tokenů, je v jistých situacích mnohem mocnější než pacc.

Na Wikipedii se můžete dočíst, že PEG je ve své podstatě jazyk pro popis recursive descent parseru. Recursive descent parser ale nezvládá levou rekurzi pravidel. phpeg používá packrat parser s podporou pro přímou levou rekurzi. (V případě, že se v gramatice nachází nepřímá levá rekurze, phpeg zahlásí chybu. Nepřímou levou rekurzi jsem ještě v žádné gramatice nepotřeboval. Až ji budu potřebovat, nejspíše se brzy dostane i do phpegu.) Packrat má ještě tu výhodu, že operuje v lineárním čase i přes neomezený backtracking – všechny mezivýsledky si ukládá (a tak je jeho minusem, že spotřebuje hodně paměti).

vydáno 7. 2. 2010, 23:33:11

1 komentář (1 nový)

Zařazeno mezi:

Cesta za ideálním šablonovacím jazykem

Začalo to problémem, pokračovalo různými inspiracemi a skončilo řešením.

Potíž s odkazovaným problémem v běžných šablonovacích systémech (Smarty-like) je ten, že jazyk, jakým jsou šablony v Smarty-like systémech psány, je imperativní. U šablon se všeobecně podle mě hodí více deklarativní způsob vyjadřování – šablonovači řeknu pouze to, co má udělat a jak to konkrétně udělá, mě nezajímá. Příklad: říct šablonovači „chci vypsat seznam položek oddělených čárkami“ je jednodušší než psát „pro každou položku v seznamu: pokud je to první položka, vytiskni pouze tuhle položku; jinak vytiskni čárku a potom teprve položku“.

Další věcí, která by se dala označit jako „jQuery idiom“ (terminus technicus jako noha), je, že nezáleží, jestli je operace aplikována na jednu, nebo více položek. Je jedno, jestli mám jen jednu položku, nebo jich je milion – já chci, aby každá položka byla obklopena tagem <b>.

A třetí podstatnou věcí je kontext. Kontext obsahuje vstupní data a zprostředkovává je šabloně.

Syntaxe šablon staví na tom, že šablony jsou bloky textu s dírami, které chtějí něčím vyplnit. Aby bylo poznat, kde díra začíná, je použit znak dolaru. Po něm následuje buď určení podkontextu, nebo filtr. Volitelně se na výsledek dají aplikovat další filtry oddělené dvojtečkou. Znak, který by oznamoval ukončení díry je podle mě nadbytečný a nenapomáhá čitelnosti.

Nejdříve případ, kdy následuje určení podkontextu. Podkontext se dá určit selektorem. Řekněme, že kontext je name = "world" a šablona:

Hello, $name!

Výsledkem bude Hello, world!. Selektor odpovídá regulárnímu výrazu [a-zA-Z0-9_]+. Vícenásobný selektor je více takových selektorů oddělených tečkami:

Hello, $person.name.first!

V kontextu person = (name = (first = "Fred")) vytiskne Hello, Fred!.

Krom selektoru může podkontext také určit pole, nebo řetězec:

$"Hello", $(1, 2, 3, 4, 5)!

Řetězec je sekvence znaků ohraničených uvozovkami. Pole je kolekce položek oddělených čárkami. Výstupem předchozího kódu v jakémkoli kontextu bude: Hello, 12345! Kontext řetězce vrací na výstup samotný řetězec. Pole vrací na výstup postupně výstup každého prvku.

To pravě ořechové začíná, když se začnou používat filtry. Nejjednodušším filtrem je aplikace anonymní šablony:

$(1, 2, 3, 4, 5):{$$, }

Jelikož anonymní šablona je filtr, je od selektoru oddělena dvojtečkou. Text šablony je ohraničen složenými závorkami. Protože kontextem pro anonymní šablonu je pole, je šablona aplikována na každou položku a výsledkem je pole nových položek. $$ je aktuální kontext. Výsledkem šablony bude: 1, 2, 3, 4, 5, . Krom $$ je dalším speciálním kontextem $# který obsahuje klíč pole:

$(foo = "bar", "bar" = "baz"):{$# = $$, }

Výsledek: foo = bar, bar = baz, .

Filtrem může být také pojmenovaná šablona:

$"foo":#justprint{$$}

Výhoda pojmenované šablony je v tom, že ji nadále můžeme používat pouze pomocí jejího jména. Pokud tedy předchozí šablonu upravíme na:

$"foo":#justprint{$$}
$"bar:justprint

Výstupem bude:

foo
bar

Je také možno definovat pojmenovanou šablonu mimo výraz začínající dolarem:

#justprint{$$}
$"foo":justprint
$"bar":justprint

Výstup bude stejný jako předtím. Pojmenovaná šablona definovaná mimo výraz s dolarem nic nevytiskne, pouze se pak bude dát použít v následujících výrazech.

Posledním, vlastně už známým druhem filtru, je aplikace pojmenované funkce. V případech výše to vždycky byly šablony, které tyto filtry vytvářely, ale taková pojmenovaná funkce může být externě definovaná, někde v PHP kódu (takové funkce jsou samozřejmě mnohem mocnější). Krom toho mohou přijímat další parametr, např.:

$"foo":transform(upper):prepend{->}

Parametrem je buď pole, nebo šablona (rozdíl mezi zavoláním funkce s šablonou a aplikací pojmenované šablony je v tom, že u pojmenované šablony je na začátku mřížka). V případě šablony se jedná vlastně o jednoprvkové pole – prepend({->}).

A co když není vytvořen žádný nový kontext a za dolarem následuje už rovnou filtr? Pak se bere aktuální kontext (pozor, kdyby za justprint na druhém řádku nebyly kulaté závorky, bylo by to bráno jako selektor, nikoli jako aplikace filtru):

#justprint{$$}
$"foo":{$justprint()}

Je to samé jako:

#justprint{$$}
$"foo":{$$:justprint}

A jak by vypadal problém ze začátku v tomto šablonovacím systému?

<ul>
    $seznam:{<li><a href="/zobrazit?id=$id">$(
        "vymazat" = $vymazat,
        "upravit" = $upravit
    ):allTrue:{<a href="/$#?id=$id">$#</a>}:pss(" (", ", ", ")")</li>}
</ul>

Filtr alltrue vybere všechny pravdivé hodnoty. pss přijímá tři parametry – prefix, separator, suffix – položky pole spojí pomocí separatoru, před ně přidá prefix a za ně suffix, jednalo by se o funkci definovanou někde mimo šablony.

Jak by mohlo vypadat vypsání tabulky:

<table>
    <tr>
        <th>Jméno</th>
        <th>Heslo</th>
    </tr>

    $uzivatele:{
    <tr class="$#:class("odd", "even")">
        <td>$jmeno</td>
        <td>$heslo</td>
    </tr>
    }
</table>

Anebo hieararchické menu:

$($menu):#menu{
<ul>
    <li>
        <a href="$url">$nazev</a>
        $($podmenu):menu
    </li>
</ul>
}

vydáno 4. 2. 2010, 11:36:06

žádný komentář

Zařazeno mezi:

pacc – parser generátor pro PHP

Někdo znovuobjevuje kolo v podobě frameworků pro PHP. Já na to jdu jinak, já znovuobjevuji parser generátory.

Když jsem narazil na CoffeeScript, popadl mě (opět) nápad začít pracovat na vlastním programovacím jazyce. Takovéhle sklony mám již delší dobu, ale naštěstí se mi to vždycky podaří utlumit a začít pro změnu dělat něco užitečného. Už jsem přešel od svého ideálního dynamického imperativního přes funkcionální dynamicky typovaný až po imperativní staticky typovaný jazyk.

U CoffeeScriptu mě zaujalo hlavně to, že se zkompiluje do JavaScriptu. Takových projektů, jak na JavaScript naroubovat jiný jazyk, je tu více. Dokonce je tu i interpret YARV bytecodu. Jelikož mojí platformou volby je PHP. A PHP není zrovna jeden z jazyků, které by se mi líbily. Je to paradox – dva jazyky se mi opravdu moc nelíbí (PHP a C++) a oba jsou na první příčce seznamu, když chci něco napsat.

Inspirován CoffeeScriptem jsem se rozhodl, že jazyk (až a jestli vůbec někdy bude existovat) bude kompilován do PHP.

Jelikož psát parser (zvlášť u těch složitějších) ručně je zábava na dlouhé zimní večery, rozhodl jsem se, že je potřeba parser generátor. Nebyl bych to PHPčkař, kdybych využil třebas balíček z PEARu, nebo rovnou ANTLR PHP target, a tak jsem napsal pacc.

Je to čistě parser generátor (lexer si musíte dopsat sami) a je ve stádiu „proof of concept“ (bohužel jako většina toho, co napíšu). pacc přijímá na vstupu soubor formátu, který se jistě v budoucnu ještě mnohokrát změní.

Na příklady se podívejte do podadresáře examples/ v repozitáři. Já si tu jeden vezmu a rozeberu ho podrobněji. Bude to kalkulátor (calculator.y):

grammar Calculator

Na začátku souboru je název gramatiky. (Teď uvažuji nad tím, jestli zrovna grammar je to nejvhodnější slovo…) pacc vygeneruje tedy třídu s názvem Calculator. Za deklarací grammar může, ale nemusí být středník.

option (
    eol = "\n";
    indentation = "    ";
    parse = "doParse";
    algorithm = "LR";
)

Složená deklarace option. eol nastavuje, jaký bude znak konce řádku ve vygenerovaném souboru (ve výchozím stavu je to \n), indentation zase, čím se bude odsazovat (defaultně čtyři mezery). parse udává, jak se bude jmenovat metoda, která provede vlastní parsování (výchozí doParse). algorithm je asi nejzajímavější – nastavuje totiž, jaký výstupní typ algoritmu chcete. Zatím jsou implementovány dva – RD (recursive descent) a LR (LR parser, konkrétně canonical LR(1)). Recursive descent je nejjednodušší typ parseru a většinou se píše ručně (třebas parser použitý k naparsování souboru s gramatikou je právě ručně napsaný recursive descent). S LR je to složitější, protože tam jsou potřeba tabulky a ty je nejjednodušší právě vygenerovat.

Stejně jako s grammar deklarací, za option může a nemusí být středník (option ( ... );). Ve složeném optionu středník odděluje jednotlivá nastavení, takže za posledním být může, avšak nemusí.

Krom složeného option je tu i jednoduché. Následující kód je ekvivalentní předchozímu:

option eol = "\n"
option indentation = "    "
option parse = "doParse"
option algorithm = "LR"

Krom řetězce (přičemž řetězec je sled znaků ohraničený uvozovkami, apostrofem, nebo obráceným apostrofem) lze jako option též nastavit PHP kód. Ale pro toto je jednodušší využít syntaktického cukru:

@inner {
    const NUMBER = 1;

    private $expression;
    private $token;

    public function calculate($expression)
    {
        $this->expression = $expression;
        $this->_nextToken();
        return $this->doParse();
    }
}

@footer {
    $calculator = new Calculator;
    echo $calculator->calculate(file_get_contents('php://stdin')) . "\n";
}

PHP kód je vždy ohraničen složenými závorkami. Zavináč, nějaký název a PHP kód nastavuje danou možnost na PHP kód. Kód inner se vloží do těla vygenerované třídy, typicky obsahuje nastavení instančních a třídních proměnných a veřejných metod pro manipulaci s parserem; footer bude umístěn na konec souboru a jeho tu za účelem inicializace, která nelze provést ve třídě (v příkladě s kalkulátorem přečteme standardní vstup a necháme ho vyhodnotit). Ještě je tu header, kterýžto není sice v příkladu uveden, ale v případě, že by byl, tak se vloží nad třídu, může obsahovat např. doc-komentář.

@currentToken {
    return $this->token;
}

@currentTokenType {
    if (preg_match('~^[0-9]+$~', $this->token)) { return self::NUMBER; }
    return NULL;
}

@currentTokenLexeme {
    return $this->token;
}

@nextToken {
    if (!preg_match('~^([0-9]+|\(|\)|\+|-|\*|/)~', $this->expression, $m)) {
        $this->expression = NULL;
        $this->token = NULL;
        return;
    }

    $this->token = $m[1];
    $this->expression = substr($this->expression, strlen($m[1]));
}

Čtveřice kódů, které slouží pro komunikaci s lexerem. U kalkulátoru žádný speciální lexer není, a tak tyto kódy jsou vlastně lexerem. Každý z těchto kódů bude ve výsledku metodou ve třídě, akorát s podtržítkem na začátku (currentToken bude _currentToken, currentTokenType bude _currentTokenType atd.).

currentToken by měl vracet něco (je jedno co), co reprezentuje aktuální token. V tomto případě je to řetězec. currentTokenType typ aktuálního tokenu a currentTokenLexeme obsah, řetězcovou hodnotu aktuálního tokenu (jak to má konkrétně s těmito metodami být povím u vysvětlování pravidel). Po zavolání nextToken by všechny předchozí metody měly vracet další token / informace o dalším tokenu v streamu.

Nyní již k samotným pravidlům:

expression
    : /* nothing */ { $$ = 0; }
    | component { $$ = $1; }
    | expression '+' component { $$ = $1 + $3; }
    | expression '-' component { $$ = $1 - $3; }
    ;

component
    : factor { $$ = $1; }
    | component '*' factor { $$ = $1 * $3; }
    | component '/' factor { $$ = $1 / $3; }
    ;

factor
    : NUMBER { $$ = intval($1); }
    | '(' expression ')' { $$ = $2; }
    ;

Bezkontextové gramatiky se popisují řadou produkcí s jedním neterminálem na levé straně a žádným, či více neterminály a/nebo terminály na straně pravé. Produkce navíc může mít na levé i pravé straně stejný neterminál, což znamená, že je produkce rekurzivní. Jedno pravidlo se skládá z levé strany produkcí (tedy neterminálu), poté dvojtečky, jedné nebo více pravých stran produkcí oddělených svislítkem (|) a je ukončené středníkem. U každé pravé stranu produkce může být navíc přidružen speciální PHP kód.

pacc používá Bison(-like) notaci pro zápis pravidel, což je právě ta popsaná výše. Neterminály jsou všechny řetězce vyhovující regulárnímu výrazu [a-z][a-z0-9_], terminály jsou ohraničené řetězce jako v případě nastavování optionů, nebo sekvence znaků vyhovující [A-Z][A-Z0-9_]. Pokud je u neterminálu použit ohraničený řetězec, porovnává se s hodnotou vrácenou z metody currentTokenLexeme, jestliže se jedná o druhý případ, porovnává se s hodnotou vrácenou z currentTokenType. Pokud currentToken, currentTokenLexeme i currentTokenType vrací NULL, znamená to konec streamu.

K samotným pravidlům. Začněme od factor. factor je (dvojtečku můžeme číst jako „je“) buď terminál NUMBER (porovnává se s currentTokenType), nebo (svislítko můžeme číst jako „nebo“) výraz ohraničený kulatými závorkami.

Co je toto?

$$ = intval($1);

currentToken kalkulátoru vrací vrací číslo vypreparované z řetězce určeného k vyhodnocení. Funkce intval() převede řetězec na číslo. Proměnná s názvem 1 je speciální proměnná, ve které je uložen první symbol z pravé strany produkce; analogicky 2 je druhý, 3 je třetí atd. Dolarová proměnná je výsledek produkce – tedy neterminál na levé straně. A tak výraz výše znamená, že výsledkem produkce je číselná hodnota prvního symbolu. První symbol je terminál NUMBER, který je reprezentován číselným tokenem, takže výsledkem produkce je číselná hodnota tohoto tokenu. V případě druhé větve pravidla factor, je výsledkem hodnota druhého symbolu, tedy expression.

Stejně je to s dalšími pravidly. PHP kód u pravé strany produkce nemusí být přítomen. V tomto případě je výsledkem produkce hodnota prvního symbolu.

Kalkulátor si můžete vyzkoušet. Stáhnětě si zdrojové kódy z repozitáře, přejděte do adresáře s nimi a spusťte:

$ ./bin/pacc -i ./examples/calculator.y -fo ./calculator.php
$ echo "22*2-2" | php -f ./calculator.php
42

Praktičtější z příkladů je parser JSONu, který se může hodit v případě, že na serveru není nainstalován PHP json modul.

V budoucích verzích bych chtěl vychytat co nejvíce mušek (nepředpokládám, že by jich tam bylo zrovna málo) a nakonec ručně psaný parser pro tento formát souboru úplně nahradit parserem generovaným paccem. Taky se mi zrovna nelíbí metody currentToken apod., takže tohle může doznat dramatických změn. Psát lexery mě už taky nebaví, takže podpora jeho generování může do paccu přibýt. Dalším omezením je, že první pravidlo v souboru se nesmí jmenovat grammar, nebo option – parser by pak nepoznal, že se jedná o pravidlo a myslel si, že je to nějaká direktiva, což by skončilo parser errorem. Alternativou k tomuto omezení by bylo buď všechny deklarace něčím prefixovat, nebo pravidla do něčeho obalit. Jedno horší jak druhé, takže tu tohle omezení zůstane. Vývoj bude pravděpodobně nadále probíhat na LR parseru a RD algoritmus bude nakonec odstraněn.

vydáno 13. 1. 2010, 23:21:08

2 komentáře (2 nové)

Zařazeno mezi:

Kniha Coders at Work

Coders at Work napsal Peter Seibel (autor knihy Practical Common Lisp) a kniha obsahuje 15 rozhovorů s různými programátory. Některé jména mi byla známá (Douglas Crockford, Joe Armstrong, Ken Thompson, Donald Knuth), o ostatních jsem nevěděl nic. Nejvíce mě zaujaly rozhovory s těmi právě jmenovanými.

Coders at Work

Rozhovory se zabývaly tím, jak zpovídaný přišel k programování, jak se staví k testování, za co se považuje (inženýra, umělce, nebo řemeslníka) a pak samozřejmě tím, čím se daná osoba proslavila / čím je známá.

Nejvíce se mi líbily části právě o testování aplikací a pak u Armstronga, Thompsona a Knutha o jejich činnosti. Uklidnilo mě, že hodně lidí testuje stejně jako já – tj. vypisováním si informací a zkoušením. Armstrong samozřejmě mluvil o Erlangu. (Ovšem o historii Erlangu se člověk dozví více z A History of Erlang.) Thompson o tom, jak Céčko přišlo na svět. A Knuth byl zpovídán z jeho veledíla – The Art of Computer Programming. Líbilo se mi, když řekl, že těch několik kniha napsal pro to, aby si dané věci nemusel pamatovat.

vydáno 24. 11. 2009, 17:53:04

žádný komentář

Zařazeno mezi:

Frontend-backend problém

Nikdy nepodceňujte to, co se vám urodí v hlavě, když ležíte ve vaně! Nevím, jestli to bylo pěnou vlétnuvší do nosu, nebo uvolněním celého těla včetně mysli zapříčiněným horkou vodou, avšak hlavou mi projela otázka, co je lepší/jednodušší/efektivnější způsob vývoje – frontend první, nebo backend první?

U webových aplikací je to povětšinou tak, že existuje frontend, který je zobrazován „běžným smrtelníkům“, řekněme uživatelům, kterýmžto je poskytována služba (ať se jedná o možnost něco koupit, prodat, přečíst si), kterou někteří i využijí; a pak backend, k němuž mají přístup jen „ti nejpovolanější“, administrátoři, kteří se starají o to, aby měli uživatelé co k užití.

Opět ve většině případů se jedná o více, či méně oddělené části. Je vůbec záhodno je vyvíjet odděleně? Podle mě ano – každá slouží pro někoho jiného, a proto v uživatelském rozhraní je kladen důraz na něco jiného. Na frontendu se může grafik řádně vyřádit a kvůli přístupnosti musí být kladem důraz na to, aby šel používat i bez různých AJAXovátek. Taky vidím potřebu být více výřečný – ne každý potencionální uživatel je počítačový expert. U backendu nebude střídmější grafika na škodu – hlavní je umožnit administrátorovi udělat co nejrychleji, co je potřeba; proto AJAXovátka jsou vítána (od administrátora/-ů se už může vyžadovat, aby jeho/jejich prohlížeč splňoval daná kritéria).

Frontend i backend tedy vyvíjejme odděleně. Co bude lepší, začít s frontendem, nebo s backendem?

Řekněme, že první přijde na řadu frontend. Vžijeme se do uživatele webové aplikace (ne, metodě, co by chtěl jakýsi záhadný „běžný uživatel“, nevěřím) a začneme vyvíjet. Datový model může doznat velkých změn podle toho, jak zkoušíme, co vyhovuje uživateli víc (může se do toho přidat i uživatelské testování), a pořád je tak potřeba generovat nová a nová „ukázková“ data (což může být při nějakých složitějších strukturách nesnadné /např. při použití traverzování kolem stromu/). Při začatí backendem je to naopak – máme jednoduše získaná ukázková data (o všechnu složitou manipulaci se stará daný kód), ale nemáme, kde bychom je ukazovali a nakonec zjistíme, že tahle data se ani použít nedají (přichází tak na řadu refaktoring kódu backendu).

U začatí backendem nevím, co je větší problém – že se může změnit datový model při dělání frontendu, nebo že není, co prezentovat zákazníkovi. Resp. on je k prezentování ten backend. Ale jen těžko odpovědět na otázku: „Dobře, ale já chtěl e-shop; jak se v tom tedy nakupuje?“ Ovšem podobná otázka může nastat i při přístupu z opačného konce („A jak můžu spravovat zadané objednávky?“). A ne, odpovědí není ještě více omezit funkčnost prototypu – to už nejde; stejně jako je to s bičíkem bakterií (a máš to evoluce!), je nějaký jistý základ, který pro to, aby to vůbec fungovalo, potřebujete. A navíc, o to se nejedná; i kdyby to šlo více osekat, stále je tu otázka, jestli začít front-/backendem.

Mimochodem, chtěl jsem tohle nazvat jako problém čtenáře a zapisovatele (reader writer problém), ale tohle už někdo ukradl dříve. Uživatel je totiž ten, kterému je z převážné většiny něco prezentováno, a administrátor je ten, kdo zapisuje. I když se pozice „převážně čtenáře“ se u dnešních sociálních aplikací posouvá k vyvážení čtení a zapisování (možná i převaze zapisování) z pohledu uživatele; administrátor má spíše pozici korektora/usměrňovače/mazače (i tak /právě proto/ ale budou na administraci kladeny jiné nároky než na frontend).

vydáno 5. 11. 2009, 21:58:01

žádný komentář

Zařazeno mezi: