Common Lisp è un linguaggio progettato per lo sviluppo di applicazioni grandi e complesse. Quello che negli anni '80 si pensava fosse una grande applicazione. Ma ha ottenuto da sistemi di produzione diverse strutture per gestire gli errori e persino un certo supporto per il controllo in fase di compilazione. Ancora un sacco di codice è scritto per software prototipo, sistemi di ricerca e/o scopi personali. Non trovi sempre un alto livello di qualità. Tieni anche presente che a volte un controllo molto severo può rendere un codice troppo rigido (ad esempio: molti client HTTP invieranno richieste non conformi, ma è così che è e non è possibile rifiutarli facilmente senza perdere un gran numero di possibili utenti). sguardo
Let alcuni esempi di come il Common Lisp aiuta a scrivere software robusto:
tipizzazione forte e il tipo di esecuzione controllando
ci aspettiamo che un normale sistema di Lisp farà controlli runtime per ogni operazione. Evitare i sistemi Lisp che non lo fanno.
Se si dispone di una funzione numerica:
(defun foo (n x)
....
(bar ...))
(defun bar (a b)
(+ a b))
Se FOO
non fa controlli di argomento, ci aspettiamo che alla fine l'operazione +
controllerà gli argomenti.In fase di esecuzione si verificherà un errore e verrà eseguito un gestore errori che, per impostazione predefinita, chiamerà un debugger.
Pensateci: tutte le operazioni (la maggior parte) verranno verificate in fase di esecuzione. Tutti gli oggetti hanno un tag di tipo primitivo (intero, stringa, matrice, vettore di bit, carattere, flusso, ...) e in fase di esecuzione il tipo verrà eventualmente controllato.
ma ci aspettiamo più dal runtime Lisp:
- matrice delimita controlli
- tipo fessura assegni
- consistenza mucchio in caso di errori
- varie Protezioni contro nocivi come ridefinire funzioni standard, eliminazione del pacchetto Common Lisp, errori aritmetici, ecc.
Utilizzo di un sistema Lisp gambo che non fa controlli di tipo runtime è un enorme dolore. Ora, Common Lisp ci consente di dichiarare parti del codice che non eseguono controlli di runtime. Strategia migliore: trovare la più piccola quantità di codice dove può essere eseguita senza creare un rischio (vedere LOCALLY
).
liste di argomenti
Common Lisp permette una certa lista di argomenti il controllo in fase di compilazione. Usalo.
(defun foo (&key (n 1) (x 1.0))
...)
Ora un compilatore tipico prenderà una chiamata come (foo :y 2 :x 2.0)
con un errore: argomento sbagliato parola chiave :y
.
Lascia che il compilatore verifichi che l'elenco degli argomenti abbia il giusto numero di argomenti e che vengano utilizzati gli argomenti della parola chiave corretta.
CLOS, il Lisp Object sistema comune
Usa CLOS.
(defmethod foo ((n integer) (x float)) ...)
Se si definisce un metodo come sopra, in fase di esecuzione nel corpo metodo n
sarà un numero intero e x
sarà un galleggiante. Se chiami FOO
con altri tipi di argomenti e nessun metodo, allora otteniamo un errore di runtime.
Simili per gli slot di esempio: è possibile dichiarare i tipi.
(defclass bar()
((x :type float)
(n :type integer)))
Utilizzare un'implementazione Common Lisp che controlli effettivamente tali dichiarazioni o scriva i propri assegni.
Inoltre: non creare strutture di dati non elaborate basate su elenchi. Inserirli sempre in classi e metodi CLOS. In questo modo si ottiene la giusta quantità di funzionalità di controllo e introspezione.
Controlla i tipi in fase di esecuzione
Common Lisp fornisce una macro per il tipo di runtime controllo: CHECK-TYPE.
(defun foo (n x)
(check-type n integer)
(check-type x float)
(* (isqrt n) (sqrt x)))
Il CHECK-TYPE
macro consente tipo di fantasia controllo e anche riparare l'errore.
CL-USER 27 > (foo 2000 5)
Error: The value 5 of X is not of type FLOAT.
1 (continue) Supply a new value of X.
2 (abort) Return to level 0.
3 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
CL-USER 28 : 1 > :c 1
Enter a form to be evaluated: 5.0
Si noti che è possibile utilizzare i tipi per specificare elementi come l'intervallo per numeri, dimensioni di matrice o simili.
Ad esempio questo controlla che un oggetto associato alla variabile a1
è una matrice bidimensionale di dimensioni 3 a 3:
(check-type a1 (array * (3 3)))
noti che è possibile definire tipi con DEFTYPE
con predicati tipo arbitrari.
Uso Lisp costruisce quali errori
Ad esempio ecase
vs case
segnale:
CL-USER 37 > (let ((code 10))
(ecase code
(1 'fine)))
Error: 10 fell through ECASE expression.
Wanted one of (1).
ecase
segnala automaticamente un errore, quando nessuna clausola è la corrispondenza.
La macro ASSERT
consente di verificare asserzioni arbitrarie.
Common Lisp fornisce una macro integrata ASSERT.
(defun foo (n x)
(assert (and (integerp n) (evenp n)) (n))
(assert (floatp x) (x))
(* (isqrt n) (sqrt x)))
Anche in questo caso, certa quantità di riparazione di runtime è disponibile:
CL-USER 33 > (foo 2001 5.0)
Error: The assertion (AND (INTEGERP N) (EVENP N)) failed.
1 (continue) Retry assertion with new value for N.
2 (abort) Return to level 0.
3 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
CL-USER 34 : 1 > :c 1
Enter a form to be evaluated:
2000
98.38699
Usa CLOS per semplice Design by Contract
(defclass bar()
((n :type integer)
(x :type float)))
(defmethod setup-bar ((b bar) (n1 integer) (x1 float))
(with-slots (n x) b
(setf n n1 x x1))
b))
Ora possiamo scrivere un metodo in più per verificare la presenza di esempio che n
è maggiore di x
:
(defmethod setup-bar :before ((b bar) (n1 integer) (x1 float))
(assert (> n x) (n x)))
Il : prima metodo sarà sempre eseguito prima il metodo principale.
Aggiungi un Design by Contract sistema di CLOS
Non ci sono librerie per questo. Quid Pro Quo è un esempio. C'è anche un'implementazione DBC più semplice e precedente di Matthias Hölzl: Design by Contract.
gestione degli errori avanzata con la condizione del sistema
tipi di scrittura di condizione:
(define-condition mailer-incomplete-delivery-error
(mailer-error)
((recipient-and-status-list :initarg :recipient-and-status-list
:reader mailer-error-recipient-and-status-list)))
Sopra è una condizione nuova, in base alla condizione mailer-error
. A runtime possiamo prendere un codice di risposta SMTP e segnalare tale condizione.
Scrivere gestori e riavvii per gestire gli errori. È avanzato. L'uso estensivo del sistema di condizioni di solito indica un codice migliore.
Scrivere e controllo test
In molti casi codice robusto ha bisogno di una suite di test. Common Lisp non fa eccezione.
Facciamo gli errori Segnala utente
In molti implementazione Common Lisp si può ottenere l'oggetto condizione di errore, un backtrace e alcuni dati ambientali. Scrivili in un log degli errori. Consenti all'utente di segnalarli. Ad esempio, LispWorks ha il comando :bug-form
nel debugger.
Solo per aggiungere al progetto per contratto, [quid-pro-quo] (https://github.com/sellout/quid-pro-quo) aggiunge a CLOS come * runtime * controlla – PuercoPop
Una risposta fantastica, migliore di quanto avrei potuto sperare. –
Potrei aggiungere: le asserzioni CHECK-TYPE aiutano anche il compilatore ad ottimizzare il codice. Molti compilatori CL sono "intelligenti" abbastanza da dedurre che, se 'x' ha passato' (check-type x (intero 0 10)) ', che possono quindi ottimizzare il codice successivo in quella funzione solo per gli interi da 0 a 10 Spesso, vinci due volte da queste dichiarazioni. – BRFennPocock