Vai al contenuto

Codice commentato

Pubblicato:

Una cosa che accomuna tutti i linguaggi di programmazione, ma persino linguaggio di markup come HTML, è la possibilità di inserire dei commenti nel codice. I commenti sono delle parti di codice che vengono completamente ignorate da qualsiasi software stiamo usando per eseguire/parsare il testo. L’unica funzionalità che offrono, quindi, è quella di spiegare in maniera più o meno dettagliata ad altri umani le sezioni di codice a cui sono associati.
Sui commenti ho sentito le opinioni più disparate. Da chi li considera una perdita di tempo, a chi li considera uno strumento fondamentale e imprescindibile per qualsiasi progetto che si rispetti. Come molti pattern di programmazione, penso che tutto dipenda dal contesto. Provo quindi a fare un po’ di chiarezza con qualche esempio.

Keep it simple, stupid (KISS)

Scrivere del codice chiaro e ben strutturato è una delle abilità più importanti che un programmatore possa sviluppare, e purtroppo richiede anni di esperienza e pratica. La scelta dei nomi delle variabili, la struttura dei file, l’uso di funzioni e classi, sono tutte scelte che possono facilitare o ostacolare la comprensione del codice.

Per fare un esempio volutamente estremo, se messi davanti a questo snippet di codice in Python,

@lambda _: _()
class _:
    def __format__(_, __):
        _.__class__._ = property(lambda _: print(__))
        return ""

fest 

non
def __() -> f"{_:Hello, world!}": ...

_._

ben pochi riuscirebbero ad intuire il suo funzionamento, quando in realtà si tratta di qualcosa di banale:

def print_hello_world():
    print("Hello, world!")

print_hello_world()

Questa semplicissima osservazione ci porta a una prima regola

Note

Non scrivere codice complicato quando non è necessario.

Chiamiamo le cose con il loro nome

Ci sono due cose difficili in informatica: invalidazione della cache e dare nomi alle cose.

— Phil Karlton

Sebbene sia una attività notoriamente antipatica, la scelta dei nomi delle variabili e delle funzioni è uno strumento utilissimo per rendere chiari il funzionamento e lo scopo del codice ad un colpo d’occhio. Prendiamo ad esempio il seguente codice:

def fun(a0, a1):
    a2 = 1
    for a3 in range(a1):
        a2 *= a0
    return a2

Prestando un po’ di attenzione, non è poi così difficile comprendere cosa faccia questa funzione, ma se la mettiamo a confronto con il suo equivalente

def pow(base, exp):
    result = 1
    for _ in range(exp):
        result *= base
    return result

la differenza è abissale. Il nome pow (o power) ci dice immediatamente che si tratta di una funzione per l’elevamento a potenza, e i nomi delle variabili base, exp (o exponent) e result comunicano implicitamente cosa aspettarci da ognuna di esse.

Tip

In questo caso specifico, a meno che non abbiamo un motivo valido, sarebbe meglio utilizzare l’operatore ** già presente in Python.

Insomma, superando la giustificatissima pigrizia caratteristica di ogni programmatore che si rispetti, perdere qualche secondo in più per scegliere la terminologia adatta al contesto può prevenire ore di mal di testa a chiunque si ritrovi a leggere i nostri capolavori in futuro.

Note

Dare nomi significativi a tutti gli elementi del codice contribuisce alla sua comprensione intuitiva.

Un tipo di aiuto

Un altro strumento che può essere utile per rendere il codice più chiaro è l’uso di tipi espliciti. Alcuni linguaggio di programmazione che hanno avuto un enorme successo recentemente, come Python e JavaScript, fanno della loro rapidità di sviluppo e della loro flessibilità il loro punto di forza. Questo include anche un sistema di tipizzazione dinamica. Il tipo delle variabili non è indicato nel momento in cui queste vengono dichiarate, ma può cambiare in qualsiasi momento, necessitando un controllo a runtime. Sebbene possa essere piuttosto comodo per scrivere rapidi prototipi, questo approccio rende praticamente impossibile conoscere a priori il tipo di una variabile, e quindi il suo scopo e le operazioni che possiamo compiere su di essa.
Per ovviare a quello che, negli anni, è stato riconosciuto quasi universalmente come una fonte inesauribile di bug e sviste, alcuni linguaggi hanno introdotto un sistema di type-hinting che, senza introdurre vincoli stringenti, permette di dare un indizio sul tipo che ci si aspetta la variabile abbia.
Prendiamo ad esame questo codice in Python:

def pow(bases, exp):
    if isinstance(bases, (int, float)):
        return [bases ** exp]
    if isinstance(bases, list):
        return [x ** exp for x in bases]

Questa funzione calcola la potenza di un numero o di una lista di numeri, e restituisce una lista con i risultati. Anche in questo caso, vista la semplicità dell’esempio, non è difficile capire bene il tipo di parametri in input e output, ma tutto sarebbe ancora meno ambiguo se avessimo usato le annotazioni dei tipi.

def pow(bases: int | float | list[int | float], exp: int) -> list[float]:
    if isinstance(bases, (int, float)):
        return [bases ** exp]
    if isinstance(bases, list):
        return [x ** exp for x in bases]

A chi non è abituato, le annotazioni possono sembrare un po’ verbose, persino confusionarie, ma, soprattutto in progetti più grandi e se ben integrate nel proprio editor, semplificano tantissimo l’interfacciarsi con librerie esterne senza andare a dover spulciare il codice sorgente o la documentazione. Inoltre, se proprio ci si vuole male, si possono usare strumenti come mypy o pyright (o utility equivalenti in linguaggi che non siano Python) per ottenere delle garanzie in più sulla correttezza dei dati che ci aspettiamo di fornire e ricevere.

Note

Aggiungere delle indicazioni sui tipi ovunque possibile, soprattutto in linguaggi con tipizzazione dinamica, aggiunge informazioni utili alle interfacce e rende il codice meno soggetto a errori.

No Comment

Va notato che ci sono casi in cui, purtroppo, una soluzione chiara non è possibile, o semplicemente non è immediatamente evidente cosa stiamo facendo e perché. Ciò è vero soprattutto nei casi in cui:

  • bisogna ottimizzare il codice per le prestazioni. Operazioni notoriamente difficili da interpretare, come l’uso di bitwise operations o la manipolazione di puntatori possono essere necessarie.
  • l’algoritmo prevede una conoscenza piuttosto approfondita della teoria informatica o matematica alla sua base.
  • si sta implementando un algoritmo noto, ma con una variante particolare. In questo caso, è utile fare riferimento alla documentazione o al paper originale.
  • ci sono parecchi parametri che influenzano in maniera non intuitiva il funzionamento dell’algoritmo.

In questi casi i commenti diventano uno strumento fondamentale per evitare di essere costretti a perdere una quantità spropositata di tempo a fare reverse engineering di ciò che è stato scritto.

def fibonacci(n: int) -> int:
    r"""
    Calcola il n-esimo numero di Fibonacci usando la
    [formula di Binet](https://it.wikipedia.org/wiki/Jacques_Philippe_Marie_Binet).

    La formula può essere espressa come:

    $$
    F(n) = \left\lfloor \frac{\phi^n}{\sqrt{5}} + \frac{1}{2} \right\rfloor
    $$

    dove $\phi = \frac{1 + \sqrt{5}}{2}$.

    Args:
        n: Indice del numero di Fibonacci da calcolare.

    Returns:
        n-esimo numero della sequenza.
    """
    phi = (1 + 5 ** 0.5) / 2
    return int((phi ** n) / (5 ** 0.5) + 0.5)

Perdete un po’ di tempo a studiare la sintassi dei commenti del linguaggio che state usando, e cercate di usarla in maniera coerente. Molti editor si aspettano che i commenti seguano una certa struttura, come jsdoc o markdown. La speranza è che, in questo modo, vengano mostrati all’utente renderizzati in maniera decente nel momento in cui si passa il mouse sopra la funzione o la variabile.

C’è inoltre un vantaggio che personalmente ritengo estremamente sottovalutato, ovvero la generazione automatica della documentazione a partire proprio dai commenti. Vi sono tantissimi tool in grado di farlo, come Sphinx per Python e Doxygen per C/C++ e molti altri.
Con pochissimo sforzo si ottiene qualcosa che si può facilmente distribuire e che può essere consultato anche da chi non ha voglia di approfondire i dettagli implementativi.

Note

I commenti sono uno strumento fondamentale per spiegare in maniera comprensibile parti più complesse del codice che altrimenti richiederebbero una conoscenza profonda delle tecniche utilizzate e un tempo di studio non indifferente.

Conclusioni

Volendo tirare le somme di questo flusso di coscienza strutturato, spero di avervi convinto del fatto che scrivere il codice è un’attività che richiede un po’ di ragionamento in più di quanto ci si possa aspettare. Non è difficile riempire centinaia di righe di codice senza nemmeno accorgersene, ma senza un po’ di disciplina, risultato finale sarà un probabilmente incomprensibile a chiunque, incluso noi stessi dopo qualche qualche tempo.
Al contrario, se si sviluppa l’abitudine di seguire i principi che ho provato a presentare, anche se non alla lettera e sicuramente con qualche personalizzazione in base al proprio stile e gusto personale, scrivere codice chiaro e ben strutturato verrà naturale e non richiederà più alcuno sforzo.
State attenti, però, che poi non si torna più indietro!