Tuesday, January 31, 2006 

C#: in Search of Syntax

I mentioned the "library design is language design" concept in my previous post. In these days, I've been looking back at a library I wrote for C#. A library is a small language for those using it, and C# (Java is even worse) just don't allow a nice, natural syntax. In my library, I've a concept of Rule. A rule is made by a potentially complex predicate and an action. Whenever the predicate changes to true, the action is triggered. Predicate and Action are a base classes for a potentially large number of predicates and actions, some provided in the library, some user-defined.
Now, a sensible syntax to add a rule could be:
ruleSet.AddRule( Predicate, Action ) ;
where Predicate is a simple predicate like
IsChecked( box1 )
or a "composite" predicate like
IsChecked( box1 ) && ! IsChecked( box2 )
Now, C# helps by providing [a limited form of] operator overloading. But unfortunately, the code above is not legal C# if IsChecked is a class name (as it wants to be). Indeed, C# does not help in providing a good, natural syntax when creating libraries. Callers are required to put all those useless "new" around to create objects (when you can't have objects on the stack Vs. heap, "new" becomes semantically useless), disrupting the natural reading of code.
There are a few ways around this, but so far no one is really satisfactory. The best syntax I've found is creatively based on generics :-), but requires the caller to derive from a concrete base class. Therefore, thanks to the lack of multiple inheritance :-), it can't be used, as the caller is most likely already derived from Form or UserControl. I guess I need some creative approach I haven't found yet. Any ideas? :-)
Comments:
La butto lì e quindi va presa con beneficio d'inventario...

Se il "Predicate" fosse di tipo String e ne facessi il parsing?

Voleva solo essere uno spunto dato che non "maneggio" troppo bene C#, CLR, .NET in genere.
 
L'argomento e' estremamente interessante, ma a questo punto richiede un approfondimento: quando un predicato viene verificato, l'azione viene eseguita istantaneamente? Cioe' la regola viene in qualche modo registrata come osservatrice della check box, e quando questa viene azionata la regola viene notificata dell'evento? In questo caso credo che sia semplicistico pensare di poter implementare (anche in C++) una sintassi alla "assert(<metti qui una qualunque espressione booleana>)" perche' in un ambiene distribuito comporterebbe anche la presa in considerazione di problematiche legate alla concorrenza e alle sezioni critiche del codice. In questo senso non trovo cosi' fuori luogo l'implementazione "java-like" di oggetti user-defined (funtori o quant'altro) in grado di wrappare eventuali side effects: spetta cioe' all'utente (del framework intendo..) implementare tutto cio' che e' necessario perche' i costrutti offerti dalla libreria funzionino correttamente anche in casi particolari; alla fine credo che troppo zucchero sintattico si traduca in illeggibilita' del codice, cioe' nel caso di effetto collaterali impliciti il programmatore e' tenuto a conoscere l'esatta implementazione della libreria per desumerne il comportamento, cossicche' un programmatore non troppo scafato potrebbe seriamente trovarsi nei guai! Comunque l'argomento e' estremamente interessante, e sarei felicissimo di leggere ulteriori approfondimenti!
E visto che ci sono, colgo l'occasione per ringraziarti per il tempo che dedichi a questo blog (oltre che ai tuoi articoli!), che e' uno stimolo ad approfondire sempre meglio argomenti che troppo spesso conosco solo superficialmente!
 
Romano:
purtroppo mi perderei un elemento essenziale della libreria: controllo a compile time che i predicati siano sintatticamente corretti, ovvero che combinino predicati elementari conosciuti (es. IsChecked e non IsCheched), che i parametri dei predicati siano del tipo giusto, combinati con i giusti operatori binari, ecc. Peraltro io passo parametri (che sono variabili membro) ai predicati, giocare anche con questo a livello di parsing complica troppo la vita [reflection].

Michel, andando per ordine e cercando di dire molto in poche parole:
- Il framework per come e' concepito e' pensato per girare su un client ricco e per gestire solo "regole GUI". Non e', e non voglio farlo diventare, una libreria per ambienti distribuiti, o un motore data-flow general purpose. Anche le azioni, per come concepisco io l'uso del framework, dovrebbero essere limitate a svolgere certe operazioni. Ovviamente non vi ho raccontato a cosa serve tutto il baraccone :-)) e quindi lo state solo intuendo, anche se in una certa misura si capisce. Se ci saranno progressi sul lato sintattico vi raccontero' qualcosa di piu', per ora la libreria e' "brutta" nel punto di chiamata e quindi la uso con riserbo...
- Non vorrei comunque una espressione booleana qualunque, ma una combinazione di predicati noti (che sono classi anche user defined) combinate con operatori booleani, ed una azione da scatenare. Anche le Action del mio framework sono classi potenzialmente user-defined, nel senso che alcune stanno in libreria, altre le puoi aggiungere tu derivando da Action, cosi' come puoi estendere i predicati. Se avessi necessita' di sincronizzazioni varie, le metterei nelle action, a questo punto probabilmente custom.
- Per i problemi che ho in mente io, una gestione sincrona e' perfetta ed e' in realta' la piu' sicura! Essendo una libreria di "regole GUI", ed essendo gli eventi GUI single-threaded, lato predicati non ci sono problemi di sincronizzazione.
- Paradossalmente, se volessi passare un pezzetto di codice scritto al volo, in C# 2 userei semplicemente un delegate anonimo. Certo dovrei scriverci "delegate" davanti che fa schifo ma direi meno di una sfilza di new :-). Io tuttavia voglio passare oggetti ricchi, che si occupano di sottoscrivere eventi, leggere proprieta', valutare espressioni booleane composite, ecc.
Il problema sintattico e' piu' complesso lato predicato perche' voglio espressioni complesse, ma anche lato Action, scrivere new Disable( edit1 ) dove ci starebbe bene un normalissimo Disable( edit1 ) rompe l'idea di programmare vicino al problema e lontano dall'informatica.
- sulla questione "zucchero sintattico" il problema e' molto sfaccettato. Nel senso che l'alternativa totale e' solo quella di programmare sul metallo, cosi' non hai da imparare nulla che ti mette effetti collaterali impliciti (che poi e' esattamente la critica che a fine anni 60 veniva mossa all'information hiding :-). La risposta e' sempre la stessa: equilibrio tra le forze. Nel caso specifico, il framework in se' a mio avviso risolve[rebbe] benissimo un problemino piccolo ma rognoso, che storicamente e' stato la causa di moltissimi bug (del tipo "se clicko qui e qui e poi qui e qua questo dovrebbe essere grigiato ma non lo e'), e solo quello. Peraltro, convivendo senza problemi con pezzi di codice che non lo usano, anche nella stessa form. Per ora ha "solo" una sintassi brutta :-).
- Ovviamente, nel caso specifico, questo e' solo un esempio fra tanti di come la sintassi attuale del C# (e peggio ancora Java) sia purtroppo ancora poco indicata per un vero linguaggio a due livelli, in cui creare librerie che si usano come se fossero parte integrante del linguaggio.
- sul programmatore non scafato nei guai, e' un argomento ricorrente, accennato qualche volta (piu' o meno direttamente) anche da altri in alcuni commenti, e che periodicamente emerge anche in real life. E' un tema fortemente controverso. Un giorno ci scrivo su qualcosa (per un tema solo parzialmente correlato, vedi anche il mio post "The Perils of Abstractions").
 
This post has been removed by a blog administrator.
 
La tua libreria è nell'idea qualcosa di molto simile a Adam & Eve (http://opensource.adobe.org).
Anche se la prospettiva cambia, visto che adam dispone di un parser per descrivere le regole e UI. Hai mai dato un'occhiata a queste librerie? l'idea secondo me è interessante anche se trovo il sistema nell'insieme comunque un po' criptico.
 
Fulvio: a prima vista puo' sembrare simile ad Adam ma ci sono almeno due differenze:
- una piccola e quasi "storica": la mia mini-libreria e' nata per gestire solo una parte delle problematiche GUI, ovvero le regole "complesse" di attivazione / disattivazione. Poi e' cresciuta, ed alcuni miei clienti che la usano trovano comodo usare le regole per cose "diverse" che la rendono un po' piu' simile ad Adam.
- una decisamente grande, e l'avevi gia' notata: e' una libreria che si integra con il framework .NET (teoricamente i concetti sono portabili altrove). Non e' uno strumento extralinguistico (vedi uno dei miei post precedenti). Non e' nemmeno un framework invasivo: convive allegramente con codice scritto in altro modo, anche dentro la stessa form.
Peccato che per ora la sintassi sia molto brutta e che i miei vari tentativi di migliorarla non abbiano avuto grande successo. Se riesco a renderla almeno guardabile :-) ci scrivo su qualcosa e la rendo di pubblico dominio...
 
This post has been removed by a blog administrator.
 
Post a Comment

<< Home