Come si mantiene il protocollo del valore di ritorno della funzione nel linguaggio dinamico?

3

In linguaggio di programmazione dinamico come Python, Javascript, è molto facile fare una funzione restituire un oggetto:

def make_vector2(a, b):
    return {"x": a, "y": b}

La 'firma' dell'oggetto restituito da make_vector2 funge da protocollo o interfaccia ad altre funzioni. Ad esempio:

def norm(v):
    return math.sqrt(v["x"]**2 + v["y"]**2)

Durante l'evoluzione di un programma, potrebbero esserci delle modifiche alla firma dell'oggetto restituito. Ad esempio, cambierei sopra la definizione del vettore in {"x0": a, "x1":b} .

Questa modifica del protocollo può invalidare le sue funzioni correlate (cioè norm nell'esempio sopra).

Nel linguaggio di programmazione statica, ho il compilatore che mi aiuta a mantenere il protocollo coerente. Mentre sono in linguaggio di programmazione dinamico non riesco a rilevare i problemi a meno che non eseguo effettivamente il codice.

Ci sono suggerimenti / buoni strumenti per questo problema?

    
posta TheBusyTypist 16.03.2015 - 03:17
fonte

2 risposte

3

Le persone che preferiscono le lingue dinamiche non considerano questo problema in primo luogo. gli strumenti di base utilizzati per affrontare questo sono semplicemente buoni test. Sì, non è possibile rilevare un problema come quello che descrivi senza eseguire il codice, quindi la chiave è eseguire il codice attraverso una buona suite di test, probabilmente tutte le volte che compili.

Ovviamente ci sono degli svantaggi. Se il codice non viene testato, l'errore non viene rilevato mentre un compilatore l'avrebbe rilevato. Ma ciò che si perde lì, si guadagna in velocità di sviluppo e la possibilità di utilizzare un accoppiamento più morbido.

Il tuo codice verrà ovviamente lanciato quando viene chiamato norm . Quindi, se esegui una suite di test di base che chiama norm con i risultati di make_vector2 almeno una volta, prendi l'errore. Se si eseguono questi test tutte le volte che si compila un programma C ++ o Java, allora non fa alcuna differenza. In ogni caso, il bug viene catturato.

L'altra parte della risposta è provare a progettare il codice in modo che il contratto sia più chiaro. Ad esempio:

class Vector:
    def __init__(self,x,y):
        self.x = x
        self.y = y

def make_vector2(a, b):
    return Vector(a,b)

def norm(v):
    return math.sqrt(v.x**2 + v.y**2)

Questo deve ancora essere testato allo stesso modo del codice originale, ovviamente, ma questo documenta il protocollo in un modo che la versione precedente non ha. Significa che puoi fare un help(make_vector2) e vedere che restituisce un oggetto Vector con un attributo x e un y . Ciò rende l'errore meno probabile in primo luogo.

In generale, restituire i dizionari grezzi è il migliore quando si tengono intenzionalmente le cose accoppiate liberamente e quindi si possono gestire più contenuti delle varianti.

    
risposta data 16.03.2015 - 03:59
fonte
3

Un approccio consiste nell'utilizzare un sistema di contratti software, in particolare uno con supporto per contratti di funzione e contratti di funzione di ordine superiore. L'idea generale è che usi strumenti di creazione del contratto per avvolgere una funzione con regole che specificano "Accetto questo tipo di valori e restituisco questo tipo di valore". È simile a un sistema di tipi, ad eccezione del controllo dinamico. L'implementazione più importante di un tale sistema può essere trovata nel linguaggio Racket, anche se esistono implementazioni per altri linguaggi dinamici tra cui JavaScript e Python.

    
risposta data 16.03.2015 - 04:56
fonte

Leggi altre domande sui tag