Come scrivere commenti per spiegare il "perché" dietro la funzione di callback quando i nomi di funzione e parametro sono insufficienti?

1

Come dovrei avvicinarmi ai commenti di scrittura per le funzioni di callback? Voglio spiegare il "perché" dietro la funzione quando i nomi di funzione e parametro sono insufficienti per spiegare cosa sta succedendo.

Mi sono sempre chiesto perché commenti come questo possono essere così comuni nei documenti delle biblioteche in lingue dinamiche:

/**
 * cb: callback    // where's the arguments & effects?
 */
 func foo( cb )

Forse l'atteggiamento comune è "puoi guardare il codice sorgente da solo dopo tutto" che spinge le persone a lasciare commenti minimalisti come questo.

Ma sembra che ci dovrebbe essere un modo migliore per commentare le funzioni di callback.

Ho provato a commentare i callback in modo Haskell:

/**
 * cb: Int -> Char
 */
func foo(cb)

E per essere onesti, di solito è abbastanza pulito.

Ma si mette nei guai quando ho bisogno di passare qualche struttura complessa. Il problema è in parte dovuto alla mancanza di un sistema di tipi:

/**
 * cb: Int -> { err: String -> (), success: () -> Char }  // too long...
 */
func foo(cb)

Oppure ho provato anche questo:

/**
 * cb: Int -> { err: String -> (),
 *              success: () -> Char }  // better ?
 */
func bar(cb)

Il problema è che puoi inserire la struttura da qualche altra parte, ma devi dargli un nome per farvi riferimento. Ma quando nominerai una struttura che stai per utilizzare, l'aspetto è immediatamente ridondante:

// Somewhere else...
// ResultCallback: { err: String -> (), success: () -> Char }

/**
 * cb: Int -> ResultCallback    // better ??
 */
func foo(cb)

E mi dà fastidio se seguo lo stile di commento di Java-doc poiché sembra ancora incompleto. I commenti non ti dicono nulla che non potresti vedere immediatamente osservando la funzione.

/**
 * @param cb {Function}  yeah, it's a function, but you told me nothing about it...
 * @param err {Function} where should I put this callback's argument ??
 *                       Not to mention the err's own arguments...
 */
func foo(cb)

Questi esempi sono JavaScript come con funzioni generiche e nomi di parametri, ma ho riscontrato problemi simili in altri linguaggi dinamici che consentono richiami complessi.

    
posta snowmantw 17.09.2013 - 04:26
fonte

2 risposte

1

Dipende da come stai scrivendo le tue funzioni di callback.

Se sono funzioni autonome, puoi utilizzare la stessa metodologia di commento che utilizzeresti per qualsiasi altra funzione.

Se sono funzioni simili a lambda, incorporate in altre funzioni, allora inizi a incorrere in problemi. Potresti provare a simulare i metodi di commento che useresti per una funzione autonoma. Ma per il momento in cui hai bisogno di quel livello di commenti, non dovresti usare una funzione lambda per iniziare la callback.

Le funzioni Lambda possono essere molto potenti, ma il loro scopo dovrebbe essere ovvio e non dovrebbero scorrere pagina dopo pagina.

Su una nota correlata, i nomi delle funzioni e le variabili dovrebbero essere sufficientemente descrittivi da trasmettere ciò che stanno facendo. Ad esempio, utilizzare "risultato" o "risultati" anziché "richiamata". Solo perché stai scrivendo codice generico / riutilizzabile non significa che non puoi usare termini descrittivi con i parametri.

È anche importante non mescolare i tuoi paradigmi di programmazione. Haskell (un linguaggio funzionale) ha idiomi diversi da JavaScript. Lo stile di missaggio è una cattiva idea e renderà il tuo codice più difficile da mantenere. C'è un vecchio programmatore che dice "Puoi scrivere COBOL male in qualsiasi lingua."

    
risposta data 17.09.2013 - 16:54
fonte
0

Il fatto che tu stia fornendo questo esempio con nomi generici di funzioni e parametri, e poi lamentando la mancanza di documentazione, indica un fraintendimento del punto dei nomi. Il nome di una cosa è la documentazione stessa e dovrebbe essere abbastanza descrittivo da dirti non solo il tipo della cosa, ma anche il suo ruolo .

Facciamo un esempio (in JavaScript, perché no?)

/**
 * cb1:             (this Row) ->
 * optional cb2:    (this Row) -> boolean
 */
RowSet.prototype.foo = function(cb1, cb2) {
    if (!cb2) cb2 = function() { return true; }
    for (var i = 0; i < this.length; ++i) {
        if (cb2.call(this[i]))
            cb1.call(this[i]);
    }
}

Anche con il commento del doc, e anche se questa è una funzione piuttosto breve, mi sento come se dovessi decifrarlo. Non dice nulla sulla semantica, o quando sarebbe opportuno usarla, o qualcosa del genere.

Ma se usiamo nomi non schifosi per cose:

RowSet.prototype.withEachRow = function(rowCallback, rowPredicate) {
    if (!rowPredicate) rowPredicate = function() { return true; };
    for (var i = 0; i < this.length; ++i) {
        if (rowPredicate.call(this[i]))
            rowCallback.call(this[i]);
    }
}

Trovo il secondo molto più leggibile, senza commenti. Una volta che hai nomi decenti, i commenti che erano quasi obbligatori nel primo esempio diventano rumore; tutto ciò che dicono può essere facilmente appreso leggendo un paio di righe di codice. In modo più affidabile, poiché i commenti non influiscono sulla funzionalità del programma, possono dire praticamente qualsiasi cosa. È più difficile stabilire se qualcuno ha aggiornato i commenti quando hanno cambiato la funzione. Si dovrebbe leggere la funzione per saperlo. Ma aspetta - non è quello che i commenti avrebbero dovuto aiutarci evitare ?

Inoltre, i buoni nomi diventano uno strumento molto più potente e più costantemente li usi. Ad esempio, quando si usa la parola "predicato" a sempre e solo riferirsi a una funzione che può essere chiamata su qualche elemento e restituisce un valore booleano che determina se quell'elemento corrisponde a qualche condizione, all'improvviso non è più solo un nome - è un concetto , quasi un tipo in sé.

    
risposta data 19.10.2013 - 21:02
fonte