Utilizzo della grammatica del linguaggio naturale nelle API fluenti


14

Sto armeggiando con un'astrazione di query sull'API del database WebSQL / Phonegap e mi trovo sia attratto, sia dubbioso, a definire un'API fluente che imita l'uso della grammatica della lingua inglese naturale.

Potrebbe essere più semplice spiegarlo tramite esempi. Di seguito sono tutte le query valide nella mia grammatica e i commenti spiegano la semantica prevista:

//find user where name equals "foo" or email starts with "foo@"
find("user").where("name").equals("foo").and("email").startsWith("foo@")

//find user where name equals "foo" or "bar"
find("user").where("name").equals("foo").or("bar");

//find user where name equals "foo" or ends with "bar"
find("user").where("name").equals("foo").or().endsWith("bar");

//find user where name equals or ends with "foo"
find("user").where("name").equals().or().endsWith("foo");

//find user where name equals "foo" and email is not like "%contoso.com"
find("user").where("name").equals("foo").and("email").is().not().like("%contoso.com");

//where name is not null
find("user").where("name").is().not().null();

//find post where author is "foo" and id is in (1,2,3)
find("post").where("author").is("foo").and("id").is().in(1, 2, 3);

//find post where id is between 1 and 100
find("post").where("id").is().between(1).and(100);

Modifica in base al feedback di Quentin Pradet : inoltre, l'API dovrebbe supportare forme verbali sia plurali che singolari, quindi:

//a equals b
find("post").where("foo").equals(1);

//a and b (both) equal c
find("post").where("foo").and("bar").equal(2);

Per motivi di ipotesi, supponiamo che non abbia esaurito tutti i possibili costrutti qui. Supponiamo anche che io possa coprire la maggior parte delle frasi inglesi corrette - dopo tutto, la grammatica stessa è limitata ai verbi e alle congiunzioni definite da SQL.


Modifica per quanto riguarda il raggruppamento : una "frase" è un gruppo e la precedenza è come definita in SQL: da sinistra a destra. Più gruppi potrebbero essere espressi con più whereistruzioni:

//the conjunctive "and()" between where statements is optional
find("post")
  .where("foo").and("bar").equal(2).and()
  .where("baz").isLessThan(5);

Come puoi vedere, la definizione di ciascun metodo dipende dal contesto grammaticale in cui si trova. Ad esempio l'argomento "metodi di congiunzione" or()e and()può essere lasciato fuori, oppure fare riferimento al nome di un campo o al valore previsto.

Per me questo sembra molto intuitivo, ma vorrei che tu ascoltassi il tuo feedback: è una buona e utile API o dovrei tornare indietro a un'implementazione più diretta?

Per la cronaca: questa libreria fornirà anche un'API più convenzionale, non fluente, basata su oggetti di configurazione.


1
Il concatenamento è anche il motivo per cui jQuery è molto famoso. Abbastanza diretto, sequenziale e comprensibile.
Joseph

3
Questo è interessante! Probabilmente dovresti chiedere questo ai programmatori.
Benjamin Gruenbaum,

2
Come gestiresti il ​​raggruppamento? L'equivalente di ... where foo = 1 or (bar = 2 and qux = 3):?

7
IMO questo tipo di API fluente è orribile. Ad esempio la mancanza di precedenza dell'operatore è fastidiosa. Analizzerei where("name").equals("foo").or("bar")come (name=="foo")or bar. Quindi non è chiaro quando una stringa rappresenta un valore letterale e quando presenta un nome di colonna, ...
CodesInChaos

4
btw. se si desidera utilizzare un DSL per l'interrogazione di un database, è possibile utilizzare il DSL già esistente chiamato SQL.
Codici A Caos

Risposte:


23

Penso che sia molto sbagliato. Studio la lingua naturale ed è piena di ambiguità che possono essere risolte solo con il contesto e molta conoscenza umana. Il fatto che i linguaggi di programmazione non siano ambigui è un'ottima cosa! Non penso che tu voglia cambiare il significato dei metodi in base al contesto:

  • Questo aggiunge ulteriori sorprese dal momento che porti ambiguità
  • I tuoi utenti vorranno usare costruzioni che non avrai coperto, ad es. find("user").where("name").and("email").equals("foo");
  • È difficile segnalare errori: con cosa puoi fare find("user").where("name").not().is().null();?

Supponiamo anche che io possa coprire la maggior parte delle frasi inglesi corrette - dopo tutto, la grammatica stessa è limitata ai verbi e alle congiunzioni definite da SQL.

No, non puoi coprire le frasi inglesi più corrette. Altri hanno provato prima, e diventa molto complicato molto rapidamente. Si chiama comprensione del linguaggio naturale, ma nessuno ci prova davvero: stiamo provando prima a risolvere piccoli problemi. Per la tua libreria, hai sostanzialmente due opzioni:

  • o ti limiti a un sottoinsieme di inglese: che ti dà SQL,
  • oppure provi a parlare "inglese" e scopri che non è possibile a causa dell'ambiguità, della complessità e della diversità della lingua.

Grazie per il tuo contributo. Ho modificato la mia domanda per coprire il primo caso che hai elencato e aggiunto alcuni avvertimenti. Sono d'accordo con la tua posizione sull'ambiguità: questo è il nocciolo della mia domanda. È accettabile che i linguaggi di programmazione siano ambigui in contesti ben definiti?
fencliff

se è ambiguo, non è ben definito. Se è ambiguo, c'è più di un risultato potenziale e qualcosa deve sceglierne uno. O la scelta sarà ben definita o il parser della lingua sceglierà a caso. Quindi, l'ambiguità in un linguaggio di programmazione va bene se vuoi un linguaggio stocastico (1 + 1 potrebbe essere uguale a 2, e talvolta potrebbe essere uguale a 1 o 3) rispetto a deterministico (1 + 1 è sempre uguale a 2).
Michael Paulukonis,

Stai parlando della sua proposta o dell'inglese? La maggior parte delle volte, l'ambiguità può essere risolta usando il contesto e / o la conoscenza (parte del quale è buon senso), qualcosa che non è disponibile per un computer.
Quentin Pradet,

+1 Buona cattura della sensibilità al contesto della sua particolare implementazione di un'API fluente. Mi è piaciuta l'idea della sua fluida API a prima vista, ma non ho guardato così da vicino per vederlo. Problema sicuramente enorme.
Jimmy Hoffa

se rende la grammatica non ambigua e libera dal contesto, qual è il problema? sicuramente potrebbero voler rimuovere versi singolari di forme singolari di verbi e cose del genere, ma ciò non cambia il nocciolo di ciò che stavano cercando di fare. Non avresti is()o equal()solo equals(). Non vedere il problema con la segnalazione di errori dopo questo. null()diventerebbe anche un letterale da confrontare, piuttosto che una funzione di sintassi. find("user").where("name").is().not().null();diventafind("user").where("name").not().equals(null);
quando

3

Tendo a concordare in qualche modo con i post degli altri che questo non è un grande design. Tuttavia, credo di avere ragioni diverse.

Stai presentando quella che vedo come una sintassi concreta per le query SQL. Sono fermamente convinto che la sintassi concreta non possa mai aiutare una lingua, ferire solo se è cattiva.

Tuttavia, la sintassi astratta è una storia diversa. La sintassi astratta definisce la struttura della tua lingua e il modo in cui le frasi possono essere combinate per costruire frasi più grandi. Sento che il successo di una lingua dipende fortemente dalla qualità della sua definizione di sintassi astratta.

Il mio problema con l'API fluente non è che sia ambiguo, poco chiaro o non espressivo: è che nasconde il linguaggio reale e la sua struttura e, così facendo, finisce per rendere le cose molto più complicate di quanto debbano essere ( introducendo ambiguità, errori di sintassi non ovvi, ecc.).

Dato che hai detto che fornirai anche una "API più convenzionale", sembra che tu sappia già tutto questo. Per questo dico "Bene!" Ma ciò non significa che non puoi anche sviluppare la tua API fluente in parallelo! Una singola definizione di sintassi astratta può supportare più sintassi concrete. Mentre dovresti tenere presente che la sintassi astratta è il vero affare, anche una sintassi concreta può essere molto utile.


2

Oltre agli ottimi punti di vista di Quentin Pradet, dubito dei presunti benefici di questa lingua.

Presumibilmente il punto di una grammatica vicina al linguaggio naturale è renderlo accessibile. Ma SQL è già abbastanza vicino al linguaggio naturale. Uno di questi è davvero più vicino all'inglese dell'altro?

find("user").where("name").equals("foo")

select user from table where name = 'foo'

Non vedo davvero il beneficio della tua grammatica, dal punto di vista dell'intuitività o della leggibilità. In effetti, la versione SQL sembra più leggibile (ed è più facile da digitare) a causa del suo spazio bianco.


2

Esistono molte cattive decisioni di progettazione tutt'altro che ideali che sembrano essere state prese nel considerare questa API.

La prima è la questione dell'utilità: a quale scopo serve? Questo sembra creare una struttura di dati che verrà compilata in un dialetto di SQL. Per inciso, la grammatica sembra essere un insieme limitato di SQL. La domanda di "quale vantaggio offre questo rispetto al solo utilizzo di SQL?" diventa chiave. Se è più complicato scrivere usando l'interfaccia fluente che scrivere semplicemente una stringa con l'interpolazione appropriata, allora non si scriverà usando questa API.

L'inglese è ambiguo. Tentare di modellare un'interfaccia fluida sull'inglese è una scelta sbagliata (è meglio usare il latino ). Quando sono presenti più analisi possibili dello stesso insieme di catene di chiamate, ciò crea confusione e sorpresa . Nessuna di queste sono cose buone da avere in un'API.

Ci sono più parti in SQL di quelle che questa API sta esponendo. I join (in una qualsiasi delle loro innumerevoli forme) sono notevolmente assenti dal set di esempio. Sottoquery ( foo in (select id from bar)), sindacati e raggruppamento per sono alcune delle cose che vengono spesso utilizzate. Raggruppamenti complessi di logica non sembrano essere presenti in modo intuitivo.

Se uno stava scrivendo usando questa API e poi ha scoperto che l'API non è in grado di esprimere la query desiderata, si perde tempo significativo. È una pessima scelta usare stili misti per fare una query in un'applicazione (semplici query in questo api, complesse in raw sql) - e alla fine verrà usata quella più espressiva.

Mentre la programmazione è diffusa, la padronanza della lingua inglese non lo è. Anche con una limitazione della lingua a "SQL like" ci sono sfumature su come un madrelingua leggerebbe qualcosa e qualcuno che abbia l'inglese come seconda o terza lingua.

Esiste una ridondanza non necessaria nell'API per il bene dell'inglese. In particolare equal()vs equals()fare la stessa cosa. Anche se non ne sono certo, credo che is()sia una no-op aggiunta per il bene di un inglese più vicino corrispondente. Accolgo con favore qualsiasi ascolto del mio sfogo sulla ridondanza dei metodi in ruby ​​in chat - non fare lo stesso errore.

Siediti e scrivi un set completo di esempio delle query che desideri essere in grado di utilizzare. Determina chi gestirai tutti quegli esempi in modo non ambiguo, meno ingombrante delle query stesse. Se non ci riesci, considera se vale la pena seguire il percorso di scrittura dell'API. SQL è dove si trova oggi (non è perfetto, ma non ho trovato niente di meglio) nel corso di decenni di raffinatezza.

RFC 1925 - Le dodici verità sulla rete

(12) Nella progettazione del protocollo, la perfezione è stata raggiunta non quando non è rimasto nulla da aggiungere, ma quando non è rimasto nulla da togliere.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.