La sanità dovrebbe essere una proprietà di un programmatore o di un programma? [chiuso]

4

Ho progettato e implementato linguaggi, che possono andare dalle notazioni degli oggetti ai linguaggi di marcatura. In molti casi ho considerato le restrizioni a favore della sanità mentale (conoscenza comune), come nel caso dei caratteri di controllo negli identificatori. Ci sono due conseguenze da considerare prima di fare questo:

  • Prende calcoli extra
  • Limita la libertà

Mi interessa sapere come gli sviluppatori pensano a decisioni come questa. Come forse saprai, Microsoft C # è molto aperto al contrario. Se vuoi davvero prefissare il tuo intero come Long con 'l' invece di 'L' e quindi rischiare altri sviluppatori di confondere '1' e 'l', nessun problema. Se vuoi dare un nome alle tue variabili in caratteri non latini in modo che siano in contrasto con le parole chiave latine di C #, nessun problema. O se vuoi distribuire una stringa su più righe e quindi interrompere una serie di indentazioni, nessun problema.

È economico garantire la coerenza con le restrizioni e questo rende allettante l'implementazione. Ma nel caso di non consentire caratteri non latini (riguardo al secondo esempio), significa un discredito per Unicode, perché non si sfrutterà appieno la sua capacità.

    
posta toplel32 01.06.2014 - 14:12
fonte

5 risposte

10

Non importa quanto sia restrittiva una lingua, alcuni programmatori troveranno comunque dei modi per rovinare tutto. Immagina il seguente pezzo di codice:

var price = this.ApplyVAT(basePrice);
if (this.acceptsRebate)
{
    return new RebateCalculator(RebateType.Default).Transform(price);
}

return price;

Facciamolo a pezzi:

// Rebats calculator became variable cal.
var cal = new RebateCalculator(RebateType.Default);

// stylecop complain if i dont put comment here and leave line empty. don't know why??
//
// don't know price now so later maybe!!!
Price pp = null;

// Func is transforms price to price!
Func<Price, Price> tar = cal.Do;
var g = Tax(bPR);

// changed by MAG 02/08/2013
// if arebt2 is true, the condition should match.....
if (arebt2)
{
    // assign g to g2
    var g2 = g;

    // don't remember what tar mean but its very important!!! don't remove!!!
    pp = tar(g);
}
else
{
    // it's the same!
    pp = g;
}

// i can return now because i know price!!!
return pp;

Il codice è ancora conforme allo stesso IL e non viola alcuna regola di analisi del codice StyleCop o Code (mentre StyleCop è dannatamente severo che ti obbliga a scrivere codice in modo molto preciso). Ma ora è un pezzo di merda, completamente impossibile da lavorare. Come potresti progettare una lingua che dica che quei commenti sono inutili e fastidiosi, che le mie convenzioni sui nomi sono tra le più povere e che le variabili intermedie aggiungono più danni che benefici?

L'uso di Unicode nei nomi è un buon esempio. Nel codice sopra, non ho nemmeno usato Unicode nei nomi delle variabili. [A-Za-z0-9_] è sufficiente per dare nomi senza senso. D'altra parte, Unicode può essere molto utile per migliorare il codice . Questo pezzo di codice:

const numeric Pi = 3.1415926535897932384626433832795;
numeric firstAlpha = deltaY / deltaX + Pi;
numeric secondAlpha = this.Compute(firstAlpha);
Assert.Equals(math.Infinity, secondAlpha);

è leggibile a sufficienza, ma consentire a Unicode di nominare i nomi può consentire al programmatore di scrivere questo (il problema è che non si è in grado di digitare nomi di variabili su una parola chiave non greca):

const numeric π = 3.1415926535897932384626433832795;
numeric α₁ = Δy / Δx + π;
numeric α₂ = this.Compute(α₁);
Assert.Equals(math.∞, α₂);

Alcuni sviluppatori ritengono che la seconda variante spinga troppo la denominazione, ma potrebbe essere un eccellente miglioramento della leggibilità per alcuni progetti (come un progetto scritto da un team di scienziati).

Parlando di convenzioni di denominazione, non ho visto una singola regola che impedisca effettivamente di dare nomi sfavorevoli alle variabili. Un esempio come il codice sopra può essere prevenuto da una regola simile a:

The names of private variables should contain at least four characters. The names of public variables should contain at least six characters.

Ma questo non impedirà di rinominare pp in ppppppp solo per eliminare l'avviso. Una regola aggiuntiva:

The names can't contain a character consecutively repeated more than two times. For example, feed is a valid name, while feeed is not.

può anche essere aggirato rinominando la variabile in ppabcde .

L'obiettivo effettivo

Ciò che è più importante è che un linguaggio di programmazione ben progettato dovrebbe aiutare a evitare errori . Ad esempio:

if (this.isEnabled)
    this.PlayVideo();
    this.PlaySubtitles();

è un segno che qualcosa è andato storto. Quando vedo che in un codice C #, do la colpa:

  • Il programmatore, perché è molto semplice sempre inserire parentesi graffe, invece di rischiare di perdere migliaia di dollari a causa di uno stupido bug, difficile da rilevare durante le revisioni del codice e per trovare più tardi,

  • Il programmatore, ancora una volta, perché avrebbe dovuto usare strumenti adeguati (StyleCop in questo caso),

  • La lingua, perché tale sintassi avrebbe potuto essere proibita,

  • L'IDE, perché non ha reagito al fatto che il programmatore ha rovinato il rientro.

Conclusione

  • Sii restrittivo quando aiuta a evitare bug e quando la flessibilità della sintassi non è particolarmente utile.

  • Non essere restrittivo solo per aiutare i principianti: troveranno comunque dei modi per rendere il loro codice illeggibile.

  • Aggiungi zucchero sintattico quando aiuta la leggibilità il più delle volte, senza essere usato impropriamente il più delle volte.

Esempio di Python

Se conosci Python, questo è un buon esempio di rigore utile . Le persone che hanno appena iniziato a impararlo sono generalmente negativamente sorprese dalla sua severità:

What? I can't indent the code like I want? This is annoying!

o

There are no switch statements in Python? Are you serious?! Are you telling that I have to use if-elif-else every time I need a switch?

Alcuni anni dopo, la persona confermerebbe che:

  • Avere un'indentazione significativa è un'idea eccellente, perché impedisce errori sottili (come quello con if sopra) e riduce lo sforzo di scrivere codice (invece di mettere i punti e virgola e codice di rientro, ti occupi solo di indentazione).

  • Non hanno bisogno di un interruttore, perché in ogni caso sostituiscono condizionalmente il polimorfismo e quando hanno effettivamente bisogno di un meccanismo simile a switch , un dizionario è una scelta perfetta.

risposta data 01.06.2014 - 16:15
fonte
1

Non fare di tutto per impedire il codice errato. Non coprirai mai alcun sottoinsieme consistente di tutti i modi di scrivere codice errato e ogni regola aggiuntiva costa qualcosa (tempo, denaro, capacità mentale, opportunità, complessità, ecc.). Si può scrivere FORTRAN in qualsiasi lingua, non provare a combatterlo.

Invece, fai domande positive: quanto guadagno io e l'utente limitando la lingua? Rende il parser più semplice e veloce? Semplifica davvero la lingua? Rende più facile scrivere un buon codice (al contrario di più difficile scrivere codice errato)? Ha sinergie con altre funzionalità che aggiungono benefici completamente nuovi? Pesa quei benefici contro i costi sopra citati.

    
risposta data 01.06.2014 - 14:43
fonte
0

Gli obiettivi principali in una lingua dovrebbero essere:

  1. Permetti a un programmatore di esprimere realmente ciò che vuole. Ad esempio, piuttosto che avere un piccolo numero di tipi interi, consentire a un programmatore di specificare quali caratteristiche sono importanti, inclusi intervallo rappresentabile, comportamento di avvolgimento, trapping overflow, endianness, ecc. Non forzare i programmatori a specificare proprietà che non sono interesse, ma consentire la specificazione di qualsiasi combinazione di proprietà è importante. Per i tipi a virgola mobile, consentire al codice un certo controllo sui compromessi di precisione / velocità. Consenti ai tipi di propagare in espressioni, in modo che ad es. long1 = int1 + int2; eseguirà l'aritmetica come long . (*)

  2. È necessario che i marcatori "sì è quello che intendo" siano posti in luoghi dove c'è più di una cosa che un programmatore potrebbe plausibilmente intendere, ma non nei casi in cui il significato diretto è l'unico che abbia senso. Un'inizializzazione come double someDouble = 0.1f; potrebbe corrispondere all'intenzione del programmatore, ma è più probabile un errore; sarebbe opportuno richiedere qualcosa come double someDouble = FloatToDouble(0.1f; . D'altra parte, float someFloat = 0.1; avrebbe solo un significato plausibile e dovrebbe quindi memorizzare la frazione 1/10 in someFloat nel modo più accurato possibile.

  3. Consenti ai programmatori di specificare i luoghi in cui le conversioni implicite devono essere applicate in modo più rigoroso del solito e le posizioni in cui dovrebbero essere applicate in modo più approssimativo. Ad esempio, se una lingua supporta il box automatico, consenti un mezzo attraverso il quale un parametro del metodo può dire che non dovrebbe accettare un oggetto implicitamente auto-inscatolato.

  4. Nel decidere quali tipi di costrutti impliciti dovrebbero o non dovrebbero essere legali, pensa agli effetti delle modifiche alle parti del codice. Ad esempio, se il codice è necessario per dire someField = (float)Math.Sqrt(something) anziché semplicemente someField = Math.Sqrt(something) quando someField è un float , in che modo influirà sullo sforzo richiesto per rendere someFloat un double se float precisione è risultato inadeguato?

(*) L'argomento normale che ho letto non consente ai tipi di propagarsi nelle espressioni è che conduce a situazioni ambigue che coinvolgono sovraccarichi dell'operatore. A questo suggerirei di consentire ai metodi di specificare quali sovraccarichi favorire in caso di ambiguità o di richiedere che il codice di richiamo chiarisca cosa vuole. Inoltre, data un'espressione intera come (a*b+c)/d , suggerirei di richiedere un'indicazione su come calcolare il dividendo renderebbe più chiare le intenzioni del programmatore rispetto all'applicazione di un comportamento predefinito.

    
risposta data 04.06.2014 - 00:06
fonte
0

Dipende interamente dalla filosofia del linguaggio.

C ti consente di spararti deliberatamente nel piede per ottenere le migliori prestazioni possibili. C ++ ti permette di saltare la gamba. Java è un linguaggio di schiavitù e disciplina, preferendo la cerimonia e la sicurezza rispetto alle prestazioni. Il sistema di tipo di Haskell enfatizza la dimostrazione matematica della coerenza.

    
risposta data 04.06.2014 - 00:18
fonte
-1

Non penso che tu possa creare un linguaggio "a prova di idiota". Al massimo, puoi renderlo "a prova di errore" per coloro che hanno buone intenzioni.

Quindi non investirei frustrazione computertime e sviluppatore (tempo di compilazione) o utente (runtime) nel controllare ogni piccolo dettaglio. Ovviamente verranno fatti errori e naturalmente verranno spediti bug, ma vale la pena limitare tutto e tutti nella speranza che si possa evitare (per il pieno 100%) che i pazzi siano pazzi o che gli umani commettano errori?

    
risposta data 02.06.2014 - 11:30
fonte

Leggi altre domande sui tag