Perché molte lingue non supportano i parametri con nome? [chiuso]


14

Stavo solo pensando a quanto sarebbe più semplice leggere il codice se, quando si chiama una funzione, si potesse scrivere:

doFunction(param1=something, param2=somethingElse);

Non riesco a pensare ad alcun inconveniente e renderebbe il codice molto più leggibile. So che potresti passare un array come unico argomento e avere le chiavi dell'array come nomi dei parametri, tuttavia ciò non sarebbe comunque leggibile.

C'è uno svantaggio di ciò che mi manca? In caso contrario, perché molte lingue non lo consentono?


1
puoi dare un linguaggio che dovrebbe (e può) avere parametri nominati ma non ce l'hanno?
Bryan Chen,

1
Penso che sia troppo prolisso in molti casi. Per quanto ho visto, se una lingua lo usa o meno tende a dipendere dal fatto che avrebbe un impatto positivo o negativo sulla lingua. La maggior parte dei linguaggi in stile C funziona bene senza di essa. Nel loro caso, è spesso abbastanza ovvio cosa sta succedendo comunque, e la sua assenza aiuta davvero a ridurre il disordine in generale.
Panzercrisis,

2
@SteveFallows: "Named Parameter Idiom" sembra uno strano nome per la definizione di un'API fluente (come chiamata da Martin Fowler) su una classe builder. È possibile trovare esempi dello stesso modello in uno dei primi elementi di Effective Java di Joshua Bloch .
jhominal

3
Questa non è necessariamente una domanda basata sull'opinione
Catskul,

2
Concordato. "Perché molte lingue non supportano i parametri con nome?" è più una questione di storia delle lingue che di opinione. Sarebbe opinione se qualcuno lo chiedesse come "Non sono i parametri nominati meglio?".
Andy Fleming,

Risposte:


14

Specificare i nomi dei parametri non sempre rende il codice più leggibile: ad esempio, preferiresti leggere min(first=0, second=1)o min(0, 1)?

Se si accetta l'argomento del paragrafo precedente, è abbastanza ovvio che specificare i nomi dei parametri non dovrebbe essere obbligatorio. Perché non tutte le lingue rendono l'opzione per specificare i nomi dei parametri un'opzione valida?

Mi vengono in mente almeno due motivi:

  • In generale, introdurre una seconda sintassi per un'esigenza già coperta (qui, passando parametri) è qualcosa i cui benefici devono essere bilanciati con il costo intrinseco della complessità del linguaggio introdotto (sia nell'implementazione del linguaggio che nel modello mentale del linguaggio del programmatore) ;
  • Rende la modifica dei nomi dei parametri una modifica che interrompe l'API, che potrebbe non essere conforme alle aspettative di uno sviluppatore che cambiare un nome di parametro è una modifica banale e senza interruzioni;

Nota che non conosco alcun linguaggio che implementa parametri nominati senza implementare anche parametri opzionali (che richiedono parametri nominati) - quindi dovresti stare attento a sopravvalutare i loro vantaggi di leggibilità, che possono essere ottenuti in modo più coerente con la definizione di interfacce fluide .


6
Codifico regolarmente in diverse lingue, quasi sempre devo cercare i parametri per una semplice sottostringa in una determinata lingua. È una sottostringa (inizio, lunghezza) o una sottostringa (inizio, fine) e la fine è inclusa nella stringa?
James Anderson,

1
@JamesAnderson - In che modo una sottostringa con parametri nominati potrebbe risolvere il problema? Dovresti comunque cercare i parametri per sapere qual è il nome del secondo parametro (a meno che non esistano entrambe le funzioni e la caratteristica polimorfismo del linguaggio consenta parametri dello stesso tipo con nomi diversi).
mouviciel,

6
@mouviciel: i parametri nominati non sono molto utili per lo scrittore , ma sono fantastici per il lettore . Sappiamo tutti quanto siano importanti i nomi delle variabili per la creazione di codice leggibile e i parametri denominati consentono al creatore di un'interfaccia di fornire nomi di parametri validi e nomi di funzioni utili, rendendo più semplice per le persone che utilizzano quell'interfaccia scrivere codice leggibile.
Phoshi,

@Phoshi - Hai ragione. Ho appena dimenticato l'importanza di leggere il codice quando ho scritto il mio commento precedente.
mouviciel,

14

I parametri nominati rendono il codice più facile da leggere, più difficile da scrivere

Quando sto leggendo un pezzo di codice, i parametri con nome possono introdurre un contesto che rende il codice più facile da capire. Consideriamo per esempio questo costruttore: Color(1, 102, 205, 170). Cosa diavolo significa? Anzi, Color(alpha: 1, red: 102, green: 205, blue: 170)sarebbe molto più facile da leggere. Ma ahimè, il compilatore dice "no" - vuole Color(a: 1, r: 102, g: 205, b: 170). Quando si scrive codice utilizzando parametri denominati, si trascorre una quantità di tempo non necessaria a cercare i nomi esatti: è più facile dimenticare i nomi esatti di alcuni parametri piuttosto che dimenticare il loro ordine.

Questo una volta mi ha morso quando ho usato DateTimeun'API che aveva due classi di fratelli per punti e durate con interfacce quasi identiche. Mentre DateTime->new(...)accettato un second => 30argomento, il DateTime::Duration->new(...)ricercato seconds => 30e simili per altre unità. Sì, ha assolutamente senso, ma questo mi ha mostrato che i parametri nominati ≠ facilità d'uso.

I nomi cattivi non rendono nemmeno più facile la lettura

Un altro esempio di come i parametri nominati possano essere errati è probabilmente il linguaggio R. Questo pezzo di codice crea un diagramma di dati:

plot(plotdata$n, plotdata$mu, type="p", pch=17,  lty=1, bty="n", ann=FALSE, axes=FALSE)

Si vede due argomenti posizionali per la x ed y righe di dati, e poi un elenco di parametri denominati. Ci sono molte altre opzioni con i valori predefiniti e solo quelli sono elencati i cui valori predefiniti volevo modificare o specificare esplicitamente. Una volta ignorato che questo codice utilizza numeri magici e potrebbe trarre vantaggio dall'uso di enum (se R ne avesse uno!), Il problema è che molti di questi nomi di parametri sono piuttosto indecifrabili.

  • pchè in realtà il personaggio della trama, il glifo che verrà disegnato per ogni punto dati. 17è un cerchio vuoto o qualcosa del genere.
  • ltyè il tipo di linea. Ecco 1una linea continua.
  • btyè il tipo di scatola. Impostandolo per "n"evitare che un riquadro venga disegnato attorno alla trama.
  • ann controlla l'aspetto delle annotazioni degli assi.

Per qualcuno che non sa cosa significhi ogni abbreviazione, queste opzioni sono piuttosto confuse. Questo rivela anche perché R utilizza queste etichette: non come codice autocompattante, ma (essendo un linguaggio tipizzato dinamicamente) come chiavi per mappare i valori alle loro variabili corrette.

Proprietà di parametri e firme

Le firme di funzione possono avere le seguenti proprietà:

  • Gli argomenti possono essere ordinati o non ordinati,
  • chiamato o senza nome,
  • richiesto o facoltativo.
  • Le firme possono anche essere sovraccaricate per dimensione o tipo,
  • e può avere dimensioni non specificate con varargs.

Lingue diverse arrivano a coordinate diverse di questo sistema. In C, gli argomenti sono ordinati, senza nome, sempre richiesti e possono essere varargs. In Java la situazione è simile, tranne per il fatto che le firme possono essere sovraccaricate. Nell'obiettivo C, le firme sono ordinate, denominate, richieste e non possono essere sovraccaricate perché sono solo zucchero sintattico attorno a C.

I linguaggi tipizzati dinamicamente con varargs (interfacce da riga di comando, Perl, ...) possono emulare parametri nominali opzionali. Le lingue con sovraccarico della dimensione della firma hanno qualcosa di simile ai parametri opzionali posizionali.

Come non implementare i parametri denominati

Quando pensiamo ai parametri nominati, di solito assumiamo parametri nominati, opzionali, non ordinati. L'attuazione di questi è difficile.

I parametri opzionali potrebbero avere valori predefiniti. Questi devono essere specificati dalla funzione chiamata e non devono essere compilati nel codice chiamante. Altrimenti, i valori predefiniti non possono essere aggiornati senza ricompilare tutto il codice dipendente.

Ora una domanda importante è come gli argomenti vengono effettivamente passati alla funzione. Con i parametri ordinati, gli arg possono essere passati in un registro o nel loro ordine inerente allo stack. Quando escludiamo i registri per un momento, il problema è come mettere nello stack argomenti opzionali non ordinati.

Per questo, abbiamo bisogno di un po 'di ordine sugli argomenti opzionali. Cosa succede se il codice di dichiarazione viene modificato? Poiché l'ordine è irrilevante, un riordino nella dichiarazione di funzione non dovrebbe modificare la posizione dei valori nello stack. Dovremmo anche considerare se è possibile aggiungere un nuovo parametro opzionale. Dal punto di vista degli utenti questo sembra essere così, perché il codice che non ha usato quel parametro in precedenza dovrebbe funzionare con il nuovo parametro. Quindi questo esclude ordini come l'uso dell'ordine nella dichiarazione o l'uso dell'ordine alfabetico.

Considera questo anche alla luce del sottotipo e del Principio di sostituzione di Liskov - nell'output compilato, le stesse istruzioni dovrebbero essere in grado di invocare il metodo su un sottotipo con possibilmente nuovi parametri denominati e su un supertipo.

Possibili implementazioni

Se non possiamo avere un ordine definitivo, quindi abbiamo bisogno di una struttura di dati non ordinata.

  • L'implementazione più semplice è semplicemente passare il nome dei parametri insieme ai valori. Ecco come vengono emulati i parametri con nome in Perl o con gli strumenti da riga di comando. Ciò risolve tutti i problemi di estensione sopra menzionati, ma può essere un enorme spreco di spazio, non un'opzione nel codice critico per le prestazioni. Inoltre, l'elaborazione di questi parametri nominati è ora molto più complicata della semplice estrazione di valori da uno stack.

    In realtà, i requisiti di spazio possono essere ridotti utilizzando il pool di stringhe, che può ridurre i confronti successivi delle stringhe ai confronti dei puntatori (tranne quando non è possibile garantire che le stringhe statiche siano effettivamente raggruppate, nel qual caso le due stringhe dovranno essere confrontate in dettaglio).

  • Invece, potremmo anche passare una struttura di dati intelligente che funziona come un dizionario di argomenti con nome. Questo è economico dal lato del chiamante, perché l'insieme di chiavi è staticamente noto nella posizione della chiamata. Ciò consentirebbe di creare una funzione hash perfetta o di precalcolare un trie. La chiamata dovrà comunque verificare l'esistenza di tutti i possibili nomi di parametri che è alquanto costoso. Qualcosa del genere è usato da Python.

Quindi è troppo costoso nella maggior parte dei casi

Se una funzione con parametri nominati deve essere correttamente estensibile, non è possibile ipotizzare un ordine definitivo. Quindi ci sono solo due soluzioni:

  • Rendere l'ordine dei parametri nominati parte della firma e non consentire modifiche successive. Ciò è utile per il codice auto-documentante, ma non aiuta con argomenti opzionali.
  • Passare una struttura di dati valore-chiave al chiamante, che deve quindi estrarre informazioni utili. Questo è molto costoso in confronto, e di solito si vede solo nei linguaggi di scripting senza enfasi sulle prestazioni.

Altre insidie

I nomi delle variabili in una dichiarazione di funzione di solito hanno un significato interno e non fanno parte dell'interfaccia, anche se molti strumenti di documentazione li mostreranno comunque. In molti casi vorresti nomi diversi per una variabile interna e l'argomento denominato corrispondente. Le lingue che non consentono di scegliere i nomi visibili esternamente di un parametro denominato non ne ottengono molto se il nome della variabile non viene utilizzato tenendo presente il contesto chiamante.

Un problema con le emulazioni degli argomenti denominati è la mancanza di controllo statico sul lato chiamante. Questo è particolarmente facile da dimenticare quando si passa un dizionario di argomenti (ti guarda, Python). Questo è importante perché il superamento di un dizionario è una soluzione comune, ad esempio in JavaScript: foo({bar: "baz", qux: 42}). Qui, né i tipi di valori né l'esistenza o l'assenza di determinati nomi possono essere controllati staticamente.

Emulazione di parametri nominati (in linguaggi tipicamente statici)

Usare semplicemente stringhe come chiavi e qualsiasi oggetto come valore non è molto utile in presenza di un controllo di tipo statico. Tuttavia, gli argomenti con nome possono essere emulati con strutture o letterali di oggetti:

// Java

static abstract class Arguments {
  public String bar = "default";
  public int    qux = 0;
}

void foo(Arguments args) {
  ...
}

/* using an initializer block */
foo(new Arguments(){{ bar = "baz"; qux = 42; }});

3
" it is easier to forget the exact names of some parameters than it is to forget their order" ... è un'affermazione interessante.
Catskul,

1
Il primo contro-esempio non è un problema con i parametri nominati, e più un difetto nel modo in cui i plurali inglesi mappano i nomi dei campi nelle interfacce. Il secondo contro-esempio è allo stesso modo un problema con gli sviluppatori che fanno cattive scelte di denominazione. (Nessuna scelta dell'interfaccia di passaggio dei parametri sarà di per sé una condizione sufficiente per la leggibilità - la domanda è se sia una condizione necessaria o aiuti in taluni casi).
mtraceur,

1
+1 per i punti complessivi su costi di implementazione e compromessi nel fare parametri nominati. Penso solo che l'inizio perderà le persone.
mtraceur,

@mtraceur Hai ragione. Ora, 5 anni dopo, probabilmente scriverei la risposta in modo diverso. Se vuoi puoi modificare la mia risposta per rimuovere cose meno utili. (Di solito tali modifiche sarebbero respinte in quanto contraddicono l'intento dell'autore, ma qui sto bene con esso). Ad esempio, ora credo che molti problemi relativi ai parametri nominati siano solo restrizioni dei linguaggi dinamici e dovrebbero semplicemente ottenere un sistema di tipi. Ad esempio JavaScript ha Flow e TypeScript, Python ha MyPy. Con una corretta integrazione IDE che elimina la maggior parte dei problemi di usabilità. Sto ancora aspettando qualcosa in Perl.
amon,

7

Per lo stesso motivo per cui la notazione ungherese non è più ampiamente utilizzata; Intellisense (o il suo equivalente morale in IDE non Microsoft). La maggior parte degli IDE moderni ti dirà tutto ciò che devi sapere su un parametro semplicemente passando il mouse sopra il riferimento del parametro.

Detto questo, un certo numero di lingue supporta parametri denominati, tra cui C # e Delphi. In C #, è facoltativo, quindi non è necessario utilizzarlo se non lo si desidera e esistono altri modi per dichiarare specificamente i membri, come l'inizializzazione degli oggetti.

I parametri nominati sono utili soprattutto quando si desidera specificare solo un sottoinsieme di parametri opzionali e non tutti. In C #, questo è molto utile perché non hai più bisogno di un sacco di metodi sovraccarichi per fornire al programmatore questa flessibilità.


4
Python utilizza parametri denominati ed è una caratteristica meravigliosa del linguaggio. Vorrei che più lingue lo usassero. Semplifica gli argomenti predefiniti poiché non sei più costretto, come in C ++, a specificare esplicitamente qualsiasi argomento "prima" delle impostazioni predefinite. (Come nel tuo ultimo paragrafo, è come Python se ne va senza avere un sovraccarico ... Gli argomenti predefiniti e gli argomenti nome ti consentono di fare tutte le stesse cose.) Gli argomenti nominati rendono anche il codice più leggibile. Quando hai qualcosa del genere sin(radians=2), non hai bisogno di "Intellisense".
Gort il robot il

2

Bash certamente supporta questo stile di programmazione, e sia Perl che Ruby supportano il passaggio di liste di parametri nome / valore (come qualsiasi lingua con supporto hash / map nativo). Nulla ti impedisce di scegliere di definire una struttura / record o hash / mappa che allega nomi ai parametri.

Per le lingue che includono gli hash / map / keyvalue in modo nativo, puoi ottenere la funzionalità che desideri, semplicemente adottando il linguaggio per passare hash chiave / valore alle tue funzioni / metodi. Dopo averlo provato su un progetto, avrai una visione migliore se hai guadagnato qualcosa, sia dall'aumentata produttività derivata dalla facilità d'uso, sia dalla qualità migliorata attraverso la riduzione dei difetti.

Si noti che i programmatori esperti hanno acquisito familiarità con il passaggio dei parametri per ordine / slot. Potresti anche scoprire che questo linguaggio è prezioso solo quando hai più di un paio di argomenti (diciamo> 5/6). E poiché un gran numero di argomenti è spesso indicativo di metodi (eccessivamente) complessi, potresti scoprire che questo linguaggio è vantaggioso solo per i metodi più complessi.


2

Penso che sia lo stesso motivo per cui c # è più popolare di VB.net. Mentre VB.NET è più "leggibile", ad esempio si digita "end if" invece di una parentesi di chiusura, finisce per essere più roba che completa il codice e alla fine rende più difficile da capire.

Si scopre che ciò che rende il codice più facile da capire è la concisione. Meno meglio è. I nomi dei parametri delle funzioni sono normalmente abbastanza ovvi in ​​ogni caso, e non spingono fino in fondo per aiutarti a capire il codice.


1
Non sono d'accordo con veemenza che "meno è meglio". Portati all'estremo logico, i vincitori del codice golf sarebbero i programmi "migliori".
Riley Major,

2

I parametri nominati sono una soluzione a un problema nel refactoring del codice sorgente e non intendono rendere più leggibile il codice sorgente . I parametri denominati vengono utilizzati per aiutare il compilatore / parser nella risoluzione dei valori predefiniti per le chiamate di funzione. Se vuoi rendere il codice più leggibile, allora aggiungi commenti significativi.

Le lingue che sono difficili da refactoring spesso supportano i parametri con nome, perché foo(1)si rompono quando si firma la foo()modifica, ma foo(name:1)è meno probabile che si rompano richiedendo meno sforzi da parte del programmatore per apportare modifiche foo.

Cosa fai quando devi introdurre un nuovo parametro per la seguente funzione e ci sono centinaia o migliaia di righe di codice che chiamano quella funzione?

foo(int weight, int height, int age = 0);

La maggior parte dei programmatori farà quanto segue.

foo(int weight, int height, int age = 0, string gender = null);

Ora, non è richiesto alcun refactoring e la funzione può essere eseguita in modalità legacy quando genderè nulla.

Per specificare genderun valore è ora HARDCODED nella richiesta di age. Come in questo esempio:

 foo(10,10,0,"female");

Il programmatore ha esaminato la definizione della funzione, ha visto che ageha un valore predefinito 0e ha utilizzato quel valore.

Ora il valore predefinito per agenella definizione della funzione è completamente inutile.

I parametri denominati consentono di evitare questo problema.

foo(weight: 10, height: 10, gender: "female");

È possibile aggiungere nuovi parametri fooe non devi preoccuparti dell'ordine in cui sono stati aggiunti e puoi modificare i valori predefiniti sapendo che il valore predefinito che hai impostato è in realtà il vero valore predefinito.

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.