Un linguaggio basato sulla quantità limitata di argomenti passati alle funzioni


16

L'idea è ispirata dal fatto che operatori come +, -,%, ecc. Possono essere visti come funzioni con uno o due argomenti passati e senza effetti collaterali. Supponendo che io o qualcun altro scriva una lingua che interrompe il passaggio di più di due argomenti e funziona anche solo tramite il valore restituito:

a) un linguaggio del genere porterebbe a un codice più semplice da comprendere?

b) il flusso del codice sarebbe più chiaro? (forzato in più passaggi, con potenzialmente meno interazioni "nascoste"

c) le restrizioni renderebbero la lingua eccessivamente voluminosa per programmi più complessi.

d) (bonus) qualsiasi altro commento su pro / contro

Nota:

Dovrebbero ancora essere prese due decisioni: la prima è se consentire l'input dell'utente al di fuori di main () o suo equivalente, e anche quale sarà la regola riguardo a ciò che accade quando si passano array / strutture. Ad esempio, se qualcuno desidera che una singola funzione aggiunga più valori, potrebbe aggirare il limite raggruppandolo in un array. Ciò potrebbe essere interrotto impedendo a un array o a una struttura di interagire con se stesso, il che consentirebbe comunque, ad esempio, di dividere ciascun numero per un importo diverso, a seconda della sua posizione.


4
Ciao. Gli elenchi di pro e contro tendono a dare risposte sbagliate. Esiste un modo per riformulare la domanda per ottenere ancora le informazioni necessarie ma in un altro formato?
MetaFight,

22
Il tuo ragionamento non ha nemmeno iniziato a dare un senso a me. Alcune funzioni hanno pochi argomenti, quindi limitiamo tutte le funzioni? Normalmente quando si propongono restrizioni arbitrarie, c'è una ragione, qualcosa da guadagnare. Non riesco a vedere cosa questo possa farti guadagnare.

2
Non che ci sia qualcosa di intrinsecamente sbagliato nelle domande "what if" (anche se a volte è difficile rispondere come ha detto @MetaFight), ma se anche tu, che hai pensato alla cosa e ti sei preoccupato abbastanza da porre una domanda, non puoi davvero nominare un beneficio, quindi sono abbastanza certo che la mia reazione iniziale di "cosa? no! è stupido perché dovresti farlo" è accurata.

6
Esistono alcune lingue che consentono solo un singolo argomento per funzione: qualsiasi cosa basata sul calcolo lambda. Il risultato di solito è una funzione prendendo un unico argomento lista o una funzione che restituisce una funzione che prende l'argomento successivo fino a che tutti gli argomenti sono stati trasformati: result = f(a)(b)…(z). Questo è il caso della famiglia di lingue ML come Haskell, ma anche concettualmente in altre lingue come Lisp, JavaScript o Perl.
Amon,

3
@Orangesandlemons: Okay, allora posso codificare un numero arbitrario di numeri interi all'interno di un singolo numero intero usando solo la moltiplicazione e l'addizione (per la codifica) e la divisione e la sottrazione (per la decodifica). Quindi, è necessario vietare anche numeri interi, o almeno moltiplicazione, addizione, divisione e sottrazione. (Una conseguenza del potere della programmazione è che puoi codificare quasi tutto usando quasi tutto, e quindi limitare le cose è davvero, molto difficile. In generale, le restrizioni non "limitano" nulla, ma infastidiscono i programmatori.)
Jörg W Mittag,

Risposte:


40

Robert C. Martin nel suo libro "Clean Code" raccomanda fortemente l'uso di funzioni con 0, 1 o 2 parametri al massimo, quindi almeno c'è un autore di libri con esperienza che pensa che il codice diventi più pulito usando questo stile (tuttavia, è sicuramente non l'autorità ultima qui, e le sue opinioni sono discutibili).

Dove Bob Martin è corretto per IMHO è: le funzioni con 3 o più parametri sono spesso indicatori di un odore di codice. In molti casi, i parametri possono essere raggruppati per formare un tipo di dati combinato, in altri casi può essere un indicatore per la funzione che fa semplicemente troppo.

Tuttavia, non penso che sarebbe una buona idea inventare una nuova lingua per questo:

  • se vuoi davvero applicare una tale regola in tutto il tuo codice, hai solo bisogno di uno strumento di analisi del codice per un linguaggio esistente, non c'è bisogno di inventare un linguaggio completamente nuovo per questo (per esempio, per C # qualcosa come 'fxcop' potrebbe probabilmente essere utilizzato ).

  • a volte, combinare i parametri con un nuovo tipo non sembra valga la pena, o diventerebbe una pura combinazione artificiale. Vedi, ad esempio, questo File.Openmetodo dal framework .Net. Ci vogliono quattro parametri, e sono abbastanza sicuro che i progettisti di quell'API lo abbiano fatto intenzionalmente, perché pensavano che sarebbe stato il modo più pratico per fornire i diversi parametri alla funzione.

  • a volte ci sono scenari del mondo reale in cui più di 2 parametri rendono le cose più semplici per motivi tecnici (ad esempio, quando è necessario un mapping 1: 1 a un'API esistente in cui si è vincolati all'uso di tipi di dati semplici e non è possibile combinare diversi parametri in un oggetto personalizzato)


16
L'odore con più parametri è spesso che i diversi parametri effettivamente appartengono insieme. Prendiamo ad esempio il calcolo dell'indice di massa corporea, BMI. È una funzione della lunghezza e del peso di una persona. f (lunghezza, peso), ma quei due parametri si uniscono davvero perché faresti mai questo calcolo con l'altezza di una persona e il peso di un'altra? Quindi, per rappresentare meglio, si otterrebbe f (persona) in cui la persona può avere un'interfaccia di peso e lunghezza.
Pieter B,

@PieterB: ovviamente, vedi la mia modifica.
Doc Brown,

5
Nitpick minuscolo e minuscolo : "potrebbe indicare un odore di codice" Un odore di codice per definizione non è solo un'indicazione che dovresti riconsiderare qualcosa, anche se alla fine non cambi il codice? Quindi un odore di codice non sarebbe "indicato". Se un aspetto specifico indica la possibilità di un problema, si tratta di un odore di codice. No?
jpmc26,

6
@ jpmc26: da un altro punto di vista, un odore di codice è un possibile problema, non l'indicazione di uno ;-) Tutto dipende dalla definizione esatta di odore di codice (e una volta che ha un odore, è andato male, non è vero? ?)
hoffmale il

3
@PieterB Qualcuno lo fa davvero? Ora devi creare una nuova istanza Person ogni volta che vuoi solo calcolare un BMI con due valori arbitrari. Certo, se la tua applicazione utilizza già persone per cominciare e ti ritrovi spesso a fare qualcosa come f (person.length, person.height) potresti pulirlo un po 'ma creare nuovi oggetti specificamente per raggruppare i parametri sembra eccessivo.
Lawyerson,

47

Esistono molte lingue che funzionano già in questo modo, ad esempio Haskell. In Haskell, ogni funzione accetta esattamente un argomento e restituisce esattamente un valore.

È sempre possibile sostituire una funzione che accetta n argomenti con una funzione che accetta n-1 argomenti e restituisce una funzione che accetta l'ultimo argomento. Applicando questo in modo ricorsivo, è sempre possibile sostituire una funzione che accetta un numero arbitrario di argomenti con una funzione che accetta esattamente un argomento. E questa trasformazione può essere eseguita meccanicamente, mediante un algoritmo.

Questo si chiama Frege-Schönfinkeling, Schönfinkeling, Schönfinkel-Currying o Currying, dopo Haskell Curry che lo studiò ampiamente negli anni '50, Moses Schönfinkel, che lo descrisse nel 1924, e Gottlob Frege, che lo prefigurò nel 1893.

In altre parole, limitare il numero di argomenti ha esattamente un impatto pari a zero.


2
Cioè se si consente la restituzione delle funzioni, ovviamente. Anche in caso contrario, tutto quello che devi fare è avere la prossima funzione chiamata nel programma principale. Vale a dire che potresti avere 1 + 1 + 1 in cui la prima aggiunta è una funzione che restituisce una funzione per aggiunta aggiuntiva, oppure può essere semplicemente chiamata due volte. Quest'ultimo stile sarebbe, in teoria, più pulito o sbaglio?

5
"ha esattamente un impatto pari a zero" - La domanda del PO era se la leggibilità del codice aumentasse o diminuisse, e immagino che non sostieni che una tale decisione di progettazione per una lingua non abbia alcun impatto su di essa, vero?
Doc Brown,

3
@DocBrown Con potenza decente e sovraccarico dell'operatore, posso prendere un linguaggio al curry e farlo sembrare un linguaggio non seccato. Esempio: f *(call_with: a,b,c,d,e) sovraccarico call_with :per iniziare una catena, ,per estendere la catena e *sull'LHS per invocare facendogli fpassare ciascuno dei contenuti della catena uno alla volta. Un sistema di sovraccarico dell'operatore sufficientemente debole rende la sintassi ingombrante, ma questo è colpa del sistema di sovraccarico dell'operatore più di ogni altra cosa.
Yakk,

In realtà, il curry di Haskell riduce una funzione con n argomenti a una funzione con un argomento che restituisce un'altra funzione con n - 1 argomenti.
Ryan Reich,

2
@RyanReich Hai ragione nel vedere un "fantasma" del "numero di argomenti" di una funzione di Haskell nella sua firma di tipo. È un fantasma piuttosto che una firma vera perché in generale non hai modo di sapere se l'ultima variabile di tipo nella firma è anche un tipo di funzione. Ad ogni modo, la presenza di questo fantasma non invalida il fatto che in Haskell tutte le funzioni accettano 1 argomento e restituiscono un valore non funzionale o un'altra funzione che accetta anche 1 argomento. Questo è incorporato nell'associatività di ->: a-> b-> c è a -> (b-> c). Quindi ti sbagli principalmente qui.
Ian,

7

Ho trascorso un po 'di tempo in queste ultime settimane nel tentativo di imparare il linguaggio J del computer. In J, praticamente tutto è un operatore, quindi ottieni solo "monadi" (funzioni che hanno solo un argomento) e "diade" (funzioni con esattamente due argomenti). Se hai bisogno di più argomenti, devi fornirli in un array, oppure inserirli in "box".

J può essere molto conciso, ma come il suo predecessore APL, può anche essere molto enigmatico, ma questo è principalmente il risultato dell'obiettivo del creatore di emulare la sintonia matematica. È possibile rendere più leggibile un programma J utilizzando nomi anziché caratteri per creare operatori.


ah, quindi consente alle matrici di interagire con se stesse.

5

Un linguaggio basato sul modo in cui vincola lo sviluppatore dipende dal presupposto che lo sviluppatore del linguaggio capisca le esigenze di ciascun programmatore meglio di quanto il programmatore capisca quelle esigenze stesse. Ci sono casi in cui questo è effettivamente valido. Ad esempio, i vincoli sulla programmazione multithread che richiedono la sincronizzazione utilizzando mutex e semafori sono considerati da molti "buoni" perché la maggior parte dei programmatori non è completamente consapevole delle complessità specifiche della macchina che questi vincoli nascondono. Allo stesso modo, pochi desiderano cogliere appieno le sfumature degli algoritmi di raccolta dei rifiuti multithread; un linguaggio che semplicemente non consente di violare l'algoritmo GC è preferito rispetto a uno che forza un programmatore a essere consapevole di troppe sfumature.

Dovresti fare una valida argomentazione sul perché, come sviluppatore del linguaggio, capisci che l'argomento passa molto meglio dei programmatori che usano il tuo linguaggio che è utile impedire loro di fare cose che ritieni dannose. Penso che sarebbe una discussione difficile da fare.

Dovete anche sapere che i programmatori potranno risolvere i vostri vincoli. Se hanno bisogno di 3 o più argomenti, useranno tecniche come il curry per trasformarli in chiamate con meno argomenti. Tuttavia, questo viene spesso a costo di leggibilità, invece di migliorarla.

La maggior parte delle lingue che conosco con questo tipo di regola sono esolang, lingue progettate per dimostrare che si può effettivamente operare con un insieme limitato di funzionalità. In particolare, gli esolang in cui ogni carattere è un codice operativo hanno la tendenza a limitare il numero di argomenti, semplicemente perché devono mantenere breve l'elenco di codici operativi.


Questa è la risposta migliore
Jared Smith,

1

Avrai bisogno di due cose:

  • Chiusura
  • Tipo di dati composito

Aggiungerò un esempio matematico per spiegare la risposta scritta da Jörg W Mittag .

Considera la funzione gaussiana .

Una funzione gaussiana ha due parametri per la sua forma, vale a dire la media (posizione centrale della curva) e la varianza (correlata alla larghezza dell'impulso della curva). Oltre ai due parametri, è necessario fornire anche il valore della variabile liberax per valutarla.

Nel primo passo, progetteremo una funzione gaussiana che accetta tutti e tre i parametri, ovvero la media, la varianza e la variabile libera.

Nel secondo passaggio, creiamo un tipo di dati composito che combina media e varianza in una cosa.

Nel terzo passaggio, creiamo una parametrizzazione della funzione gaussiana creando una chiusura della funzione gaussiana legata al tipo di dati compositi che abbiamo creato nel secondo passaggio.

Infine, valutiamo la chiusura creata nel terzo passaggio passando ad essa il valore della variabile libera x.

La struttura è quindi:

  • Valuta (calcolo)
    • ParameterizedGaussian (chiusura: la formula, più alcune variabili associate)
      • Parametri gaussiani (tipo di dati composito)
        • Valore medio)
        • Varianza (valore)
    • X (il valore della variabile libera)

1
  1. In quasi tutti i linguaggi di programmazione, è possibile passare un tipo di elenco, matrice, tupla, record o oggetto come unico argomento. Il suo unico scopo è di contenere altri oggetti invece di passarli a una funzione individualmente. Alcuni IDE Java hanno anche una funzione " Estrai oggetto parametro " per fare proprio questo. Internamente, Java implementa un numero variabile di argomenti creando e passando un array.

  2. Se vuoi davvero fare ciò di cui stai parlando nella forma più pura, devi guardare il calcolo lambda. È esattamente quello che descrivi. Puoi cercarlo sul web, ma la descrizione che aveva senso per me era in Tipi e Linguaggi di programmazione .

  3. Guarda i linguaggi di programmazione Haskell e ML (ML è più semplice). Sono entrambi basati sul calcolo lambda e concettualmente hanno un solo parametro per funzione (se strizzi un po ').

  4. L'articolo 2 di Josh Bloch è: "Considera un costruttore di fronte a molti parametri del costruttore". Puoi vedere quanto sia dettagliato , ma è un piacere lavorare con un'API scritta in questo modo.

  5. Alcune lingue hanno chiamato parametri che è un altro approccio per rendere molto più facile la navigazione di enormi firme di metodi. Kotlin ha chiamato argomenti per esempio.

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.