[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.lisp

Do you do code reviews?

swflint

10/20/2015 1:42:00 AM

Hey,

I was wondering, would you all be willing to critique the following
code? I feel like it could be cleaner and/or more efficient. Any tips?

Thanks,

Sam

Code follows:

;;;; derive.lisp
;;;;
;;;; Copyright (c) 2015 Samuel W. Flint <swflint@flintfam.org>

(defpackage #:derive
(:use #:cl)
(:export :derive
:csc
:sec
:define-equation-functions
:take-derivative))

(in-package #:derive)

;;; "derive" goes here.

(defvar *rules* '())

(defun get-expansion (expression)
(third (first
(remove-if-not #'(lambda (nte)
(let ((test (second nte)))
(apply test expression)))
*rules*))))

(defun generate-match-expression (on arity &optional (type '=))
(declare (symbol on type)
(integer arity))
(case type
(=
`(and (eq function ',on)
(= arg-count ,arity)))
(>
`(and (eq function ',on)
(> arg-count ,arity)))
(>=
`(and (eq function ',on)
(>= arg-count ,arity)))))

(defmacro def-expansion (name (on arity &optional type) (&rest arguments) &body expansion)
(let ((match-expression (if type
(generate-match-expression on arity type)
(generate-match-expression on arity)))
(test-name (intern (string-upcase (format nil "~a-test" name))))
(expansion-name (intern (string-upcase (format nil "~a-expansion" name)))))

`(progn
(defun ,test-name (function &rest arguments &aux (arg-count (length arguments)))
,match-expression)
(defun ,expansion-name (,@arguments)
,@expansion)

(push (list ',name
#',test-name
#',expansion-name)
*rules*)
',name)))

(defun derive (function)
(declare (cons function))
(let ((op (first function)))
(cond
((numberp op)
0)
((and (symbolp op)
(= 1 (length function)))
1)
(t
(let ((expansion-function (get-expansion function)))
(if (functionp expansion-function)
(apply expansion-function (rest function))
(error "Undefined expansion: ~a" op)))))))

(def-expansion mult/2 (* 2) (first second)
(cond
((numberp first)
`(* ,first ,(derive (if (listp second) second (list second)))))
((numberp second)
`(* ,second ,(derive (if (listp first) first (list second)))))
(t
`(+ (* ,first ,(derive (if (listp second) second (list second))))
(* ,second ,(derive (if (listp first) first (list first))))))))

(def-expansion mult/3+ (* 3 >=) (first &rest rest)
(derive `(* ,first ,(cons '* rest))))

(def-expansion div/2 (/ 2) (numerator denominator)
`(/ (- (* ,numerator ,(derive (if (listp denominator) denominator (list denominator))))
(* ,denominator ,(derive (if (listp numerator) numerator (list numerator)))))
(expt ,denominator 2)))

(def-expansion plus/2+ (+ 2 >=) (&rest clauses)
`(+ ,@(map 'list #'(lambda (clause)
(if (listp clause)
(derive clause)
(derive (list clause))))
clauses)))

(def-expansion minus/2+ (- 2 >=) (&rest clauses)
`(- ,@(map 'list #'(lambda (clause)
(if (listp clause)
(derive clause)
(derive (list clause))))
clauses)))

(def-expansion exp/1 (exp 1) (expression)
(if (listp expression)
`(* (exp ,expression) ,(derive expression))
(if (numberp expression)
0
`(exp ,expression))))

(def-expansion expt/2 (expt 2) (base exponent)
(if (numberp exponent)
(if (listp base)
`(* ,exponent (expt ,base ,(1- exponent)) ,(derive base))
`(* ,exponent (expt ,base ,(1- exponent))))
`(* (expt ,base ,exponent) (log ,base))))

(def-expansion log/1 (log 1) (expression)
`(/ ,(derive (if (listp expression) expression (list expression))) ,expression))

(def-expansion log/2 (log 2) (number base)
(declare (ignorable number base))
`(/ (derive (cons 'log number)) (* (log ,base) ,number)))

(def-expansion sin/1 (sin 1) (arg)
`(* (cos ,arg) ,(derive (if (listp arg) arg (list arg)))))

(def-expansion cos/1 (cos 1) (arg)
`(* (- (sin ,arg)) ,(derive (if (listp arg) arg (list arg)))))

(def-expansion tan/1 (tan 1) (arg)
`(* (expt (sec ,arg) 2) ,(derive (if (listp arg) arg (list arg)))))

(def-expansion csc/1 (csc 1) (arg)
`(* (- (csc ,arg)) (cot ,arg) ,(derive (if (listp arg) arg (list arg)))))

(def-expansion sec/1 (sec 1) (arg)
`(* (sec ,arg) (tan ,arg) ,(derive (if (listp arg) arg (list arg)))))

(def-expansion cot/1 (cot 1) (arg)
`(* (- (expt (csc ,arg) 2)) ,(derive (if (listp arg) arg (list arg)))))

(defun csc (x)
"csc -- (csc x)
Calculate the cosecant of x"
(/ (sin x)))

(defun sec (x)
"sec -- (sec x)
Calculate the secant of x"
(/ (cos x)))

(defmacro define-equation-functions (name variable equation)
(let ((derivative-name
(intern
(string-upcase
(format nil "d/d~a-~a" variable name))))
(derivative (derive equation)))
`(progn
(defun ,name (,variable)
,equation)
(defun ,derivative-name (,variable)
,derivative))))

(defmacro take-derivative (equation)
(let ((derivative (derive equation)))
`',derivative))

;;; End derive
4 Answers

Pascal J. Bourguignon

10/20/2015 2:26:00 AM

0

swflint@flintfam.org (Samuel W. Flint) writes:

> Hey,
>
> I was wondering, would you all be willing to critique the following
> code? I feel like it could be cleaner and/or more efficient. Any tips?
>
> Thanks,
>
> Sam
>
> Code follows:
>
> ;;;; derive.lisp
> ;;;;
> ;;;; Copyright (c) 2015 Samuel W. Flint <swflint@flintfam.org>
>
> (defpackage #:derive
> (:use #:cl)
> (:export :derive
> :csc
> :sec
> :define-equation-functions
> :take-derivative))
>
> (in-package #:derive)
>
> ;;; "derive" goes here.
>
> (defvar *rules* '())

What are rules? Add a type definition (eg. a defstruct)
What is *rules*? Add a docstring.

> (defun get-expansion (expression)
> (third (first
> (remove-if-not #'(lambda (nte)
> (let ((test (second nte)))
> (apply test expression)))
> *rules*))))

THIRD is meaningless here.

Notice how defstruct has a (:type list) option so that the functional
abstraction generated is still implemented as you did, with lists.


> (defun generate-match-expression (on arity &optional (type '=))
> (declare (symbol on type)
> (integer arity))

Don't use declare it doesn't do what you think. Use check-type:

(check-type on symbol)
(check-type type symbol)
(check-type arity (integer 0))

> (case type
> (=
> `(and (eq function ',on)
> (= arg-count ,arity)))
> (>
> `(and (eq function ',on)
> (> arg-count ,arity)))
> (>=
> `(and (eq function ',on)
> (>= arg-count ,arity)))))

Are you sure you want to generate NIL when type is not of
type (member = > >=)?
Either use (check-type type (member = > >=))
or use ECASE (unless you really want NIL).



> (defmacro def-expansion (name (on arity &optional type) (&rest arguments) &body expansion)

DEFEXPANSION or DEFINE-EXPANSION.


> (let ((match-expression (if type
> (generate-match-expression on arity type)
> (generate-match-expression on arity)))

You introduce here a strange special case. Why do you single out NIL
for type?


(define-expansion x (= 2 nil) ())
(define-expansion x (= 2 null) ())

Instead, you may use:

(defmacro define-expansion (name (on arity &optional (type nil typep))
(&rest arguments) &body expansion)

(if typep
â?¦)




> (test-name (intern (string-upcase (format nil "~a-test" name))))

Instead of: (string-upcase (format nil "~a-test" name))
you can write (format nil "~:@(~a-test~)" 'name)
but this is very bad because if I:
(define-expression |MyOperator| â?¦)
instead of generating |MyOperator-TEST|
you will generate MYOPERATOR-TEST.

Instead, use something like:

(defun scat (&rest string-designators)
(intern (apply (function concatenate) 'string
(mapcar (function string) string-designators))))

(scat name '-test)

If you use alexandria, there's also ALEXANDRIA:SYMBOLICATE doing the
same thing.


> (expansion-name (intern (string-upcase (format nil "~a-expansion" name)))))
>
> `(progn
> (defun ,test-name (function &rest arguments &aux (arg-count (length arguments)))
> ,match-expression)
> (defun ,expansion-name (,@arguments)
> ,@expansion)
>
> (push (list ',name
> #',test-name
> #',expansion-name)
> *rules*)
> ',name)))

AHHH! SO a rule is:

(defstruct (rule (:type list)
name test-function expansion-function)

and your (third â?¦) above was (rule-expansion-function â?¦).

Instead of PUSH, you might want to update the *rule* if we redefine one
with the same name, for example using (setf aget):

(use-package :com.informatimago.common-lisp.cesarum.list)

`(setf (aget *rules* ',name) (make-rule :name ',name
:test-function #',test-name
:expansion-function #',expansion-name))

An alternative would be to use a hash-table for *rules*, but since you
don't seem to need to find them by name, the a-list may be better.


> (defun derive (function)
> (declare (cons function))

No declare, check-type.

> (let ((op (first function)))
> (cond
> ((numberp op)
> 0)
> ((and (symbolp op)
> (= 1 (length function)))
> 1)
> (t
> (let ((expansion-function (get-expansion function)))
> (if (functionp expansion-function)
> (apply expansion-function (rest function))
> (error "Undefined expansion: ~a" op)))))))
>
> (def-expansion mult/2 (* 2) (first second)
> (cond
> ((numberp first)
> `(* ,first ,(derive (if (listp second) second (list second)))))
> ((numberp second)
> `(* ,second ,(derive (if (listp first) first (list second)))))
> (t
> `(+ (* ,first ,(derive (if (listp second) second (list second))))
> (* ,second ,(derive (if (listp first) first (list first))))))))
>
> (def-expansion mult/3+ (* 3 >=) (first &rest rest)
> (derive `(* ,first ,(cons '* rest))))
>
> (def-expansion div/2 (/ 2) (numerator denominator)
> `(/ (- (* ,numerator ,(derive (if (listp denominator) denominator (list denominator))))
> (* ,denominator ,(derive (if (listp numerator) numerator (list numerator)))))
> (expt ,denominator 2)))
>
> (def-expansion plus/2+ (+ 2 >=) (&rest clauses)
> `(+ ,@(map 'list #'(lambda (clause)
> (if (listp clause)
> (derive clause)
> (derive (list clause))))
> clauses)))
>
> (def-expansion minus/2+ (- 2 >=) (&rest clauses)
> `(- ,@(map 'list #'(lambda (clause)
> (if (listp clause)
> (derive clause)
> (derive (list clause))))
> clauses)))
>
> (def-expansion exp/1 (exp 1) (expression)
> (if (listp expression)
> `(* (exp ,expression) ,(derive expression))
> (if (numberp expression)
> 0
> `(exp ,expression))))
>
> (def-expansion expt/2 (expt 2) (base exponent)
> (if (numberp exponent)
> (if (listp base)
> `(* ,exponent (expt ,base ,(1- exponent)) ,(derive base))
> `(* ,exponent (expt ,base ,(1- exponent))))
> `(* (expt ,base ,exponent) (log ,base))))
>
> (def-expansion log/1 (log 1) (expression)
> `(/ ,(derive (if (listp expression) expression (list expression))) ,expression))
>
> (def-expansion log/2 (log 2) (number base)
> (declare (ignorable number base))
> `(/ (derive (cons 'log number)) (* (log ,base) ,number)))
>
> (def-expansion sin/1 (sin 1) (arg)
> `(* (cos ,arg) ,(derive (if (listp arg) arg (list arg)))))
>
> (def-expansion cos/1 (cos 1) (arg)
> `(* (- (sin ,arg)) ,(derive (if (listp arg) arg (list arg)))))
>
> (def-expansion tan/1 (tan 1) (arg)
> `(* (expt (sec ,arg) 2) ,(derive (if (listp arg) arg (list arg)))))
>
> (def-expansion csc/1 (csc 1) (arg)
> `(* (- (csc ,arg)) (cot ,arg) ,(derive (if (listp arg) arg (list arg)))))
>
> (def-expansion sec/1 (sec 1) (arg)
> `(* (sec ,arg) (tan ,arg) ,(derive (if (listp arg) arg (list arg)))))
>
> (def-expansion cot/1 (cot 1) (arg)
> `(* (- (expt (csc ,arg) 2)) ,(derive (if (listp arg) arg (list arg)))))
>
> (defun csc (x)
> "csc -- (csc x)
> Calculate the cosecant of x"
> (/ (sin x)))
>
> (defun sec (x)
> "sec -- (sec x)
> Calculate the secant of x"
> (/ (cos x)))
>
> (defmacro define-equation-functions (name variable equation)
> (let ((derivative-name
> (intern
> (string-upcase
> (format nil "d/d~a-~a" variable name))))
> (derivative (derive equation)))
> `(progn
> (defun ,name (,variable)
> ,equation)
> (defun ,derivative-name (,variable)
> ,derivative))))
>
> (defmacro take-derivative (equation)
> (let ((derivative (derive equation)))
> `',derivative))
>
> ;;; End derive

--
__Pascal Bourguignon__ http://www.informat...
â??The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.� -- Carl Bass CEO Autodesk

swflint

10/20/2015 5:01:00 PM

0

>>>>> Pascal J Bourguignon writes:

PJB> swflint@flintfam.org (Samuel W. Flint) writes:
>> Hey,
>>
>> I was wondering, would you all be willing to critique the following
>> code? I feel like it could be cleaner and/or more efficient. Any
>> tips?
>>
>> Thanks,
>>
>> Sam
>>
>> Code follows:
>>
>> ;;;; derive.lisp ;;;; ;;;; Copyright (c) 2015 Samuel W. Flint
>> <swflint@flintfam.org>
>>
>> (defpackage #:derive (:use #:cl) (:export :derive :csc :sec
>> :define-equation-functions :take-derivative))
>>
>> (in-package #:derive)
>>
>> ;;; "derive" goes here.
>>
>> (defvar *rules* '())

PJB> What are rules? Add a type definition (eg. a defstruct) What is
PJB> *rules*? Add a docstring.

The program was done in an LP style. However, I don't really want my
prose skills on display (they could use some improvement).

>> (defun get-expansion (expression) (third (first (remove-if-not
>> #'(lambda (nte) (let ((test (second nte))) (apply test expression)))
>> *rules*))))

PJB> THIRD is meaningless here.

PJB> Notice how defstruct has a (:type list) option so that the
PJB> functional abstraction generated is still implemented as you did,
PJB> with lists.

For some reason (OK, I don't think I've ever used DEFSTRUCT), I didn't
think to use DEFSTRUCT. I don't really care how it abstracts it away, I
just used lists because I could debug them easily.

>> (defun generate-match-expression (on arity &optional (type '=))
>> (declare (symbol on type) (integer arity))

PJB> Don't use declare it doesn't do what you think. Use check-type:

PJB> (check-type on symbol) (check-type type symbol) (check-type
PJB> arity (integer 0))

OK. What then is the difference between DECLARE and CHECK-TYPE?

>> (case type (= `(and (eq function ',on) (= arg-count ,arity))) (>
>> `(and (eq function ',on) (> arg-count ,arity))) (>= `(and (eq
>> function ',on) (>= arg-count ,arity)))))

PJB> Are you sure you want to generate NIL when type is not of type
PJB> (member = > >=)? Either use (check-type type (member = > >=)) or
PJB> use ECASE (unless you really want NIL).

Good point. I was going to use ECASE, but I decided not to do so, and
I'm not entirely sure why.

>> (defmacro def-expansion (name (on arity &optional type) (&rest
>> arguments) &body expansion)

PJB> DEFEXPANSION or DEFINE-EXPANSION.

For some reason, I thought I had an abbrev for DEF to expand into
DEFINE. Of course, it turns out I didn't. Didn't realize until now.

>> (let ((match-expression (if type (generate-match-expression on arity
>> type) (generate-match-expression on arity)))

PJB> You introduce here a strange special case. Why do you single out
PJB> NIL for type?


PJB> (define-expansion x (= 2 nil) ()) (define-expansion x (= 2
PJB> null) ())

PJB> Instead, you may use:

PJB> (defmacro define-expansion (name (on arity &optional (type nil
PJB> typep)) (&rest arguments) &body expansion)

PJB> (if typep â?¦)

Hmm. Good point. Though I think I can clean that up a little more, and
simply avoid a conditional there.

>> (test-name (intern (string-upcase (format nil "~a-test" name))))

PJB> Instead of: (string-upcase (format nil "~a-test" name)) you can
PJB> write (format nil "~:@(~a-test~)" 'name) but this is very bad
PJB> because if I: (define-expression |MyOperator| â?¦) instead of
PJB> generating |MyOperator-TEST| you will generate MYOPERATOR-TEST.

PJB> Instead, use something like:

PJB> (defun scat (&rest string-designators) (intern (apply (function
PJB> concatenate) 'string (mapcar (function string)
PJB> string-designators))))

PJB> (scat name '-test)

PJB> If you use alexandria, there's also ALEXANDRIA:SYMBOLICATE doing
PJB> the same thing.

I didn't know about that in Alexandria. Figures there's already
something there.

>> (expansion-name (intern (string-upcase (format nil "~a-expansion"
>> name)))))
>>
>> `(progn (defun ,test-name (function &rest arguments &aux (arg-count
>> (length arguments))) ,match-expression) (defun ,expansion-name
>> (,@arguments) ,@expansion)
>>
>> (push (list ',name #',test-name #',expansion-name) *rules*) ',name)))

PJB> AHHH! SO a rule is:

PJB> (defstruct (rule (:type list) name test-function
PJB> expansion-function)

PJB> and your (third â?¦) above was (rule-expansion-function â?¦).

PJB> Instead of PUSH, you might want to update the *rule* if we redefine
PJB> one with the same name, for example using (setf aget):

PJB> (use-package :com.informatimago.common-lisp.cesarum.list)

PJB> `(setf (aget *rules* ',name) (make-rule :name ',name
PJB> :test-function #',test-name :expansion-function #',expansion-name))

Noted and switched. It looks cleaner, and lets me add/modify rules
without having to restart my lisp process.

PJB> An alternative would be to use a hash-table for *rules*, but since
PJB> you don't seem to need to find them by name, the a-list may be
PJB> better.

Names are only stored for when I was debugging stuff previously, though
the thought of being able to update rules is nice.

>> (defun derive (function) (declare (cons function))

PJB> No declare, check-type.

>> (let ((op (first function))) (cond ((numberp op) 0) ((and (symbolp
>> op) (= 1 (length function))) 1) (t (let ((expansion-function
>> (get-expansion function))) (if (functionp expansion-function) (apply
>> expansion-function (rest function)) (error "Undefined expansion: ~a"
>> op)))))))
>>
>> (def-expansion mult/2 (* 2) (first second) (cond ((numberp first) `(*
>> ,first ,(derive (if (listp second) second (list second))))) ((numberp
>> second) `(* ,second ,(derive (if (listp first) first (list
>> second))))) (t `(+ (* ,first ,(derive (if (listp second) second (list
>> second)))) (* ,second ,(derive (if (listp first) first (list
>> first))))))))
>>
>> (def-expansion mult/3+ (* 3 >=) (first &rest rest) (derive `(* ,first
>> ,(cons '* rest))))
>>
>> (def-expansion div/2 (/ 2) (numerator denominator) `(/ (- (*
>> ,numerator ,(derive (if (listp denominator) denominator (list
>> denominator)))) (* ,denominator ,(derive (if (listp numerator)
>> numerator (list numerator))))) (expt ,denominator 2)))
>>
>> (def-expansion plus/2+ (+ 2 >=) (&rest clauses) `(+ ,@(map 'list
>> #'(lambda (clause) (if (listp clause) (derive clause) (derive (list
>> clause)))) clauses)))
>>
>> (def-expansion minus/2+ (- 2 >=) (&rest clauses) `(- ,@(map 'list
>> #'(lambda (clause) (if (listp clause) (derive clause) (derive (list
>> clause)))) clauses)))
>>
>> (def-expansion exp/1 (exp 1) (expression) (if (listp expression) `(*
>> (exp ,expression) ,(derive expression)) (if (numberp expression) 0
>> `(exp ,expression))))
>>
>> (def-expansion expt/2 (expt 2) (base exponent) (if (numberp exponent)
>> (if (listp base) `(* ,exponent (expt ,base ,(1- exponent)) ,(derive
>> base)) `(* ,exponent (expt ,base ,(1- exponent)))) `(* (expt ,base
>> ,exponent) (log ,base))))
>>
>> (def-expansion log/1 (log 1) (expression) `(/ ,(derive (if (listp
>> expression) expression (list expression))) ,expression))
>>
>> (def-expansion log/2 (log 2) (number base) (declare (ignorable number
>> base)) `(/ (derive (cons 'log number)) (* (log ,base) ,number)))
>>
>> (def-expansion sin/1 (sin 1) (arg) `(* (cos ,arg) ,(derive (if (listp
>> arg) arg (list arg)))))
>>
>> (def-expansion cos/1 (cos 1) (arg) `(* (- (sin ,arg)) ,(derive (if
>> (listp arg) arg (list arg)))))
>>
>> (def-expansion tan/1 (tan 1) (arg) `(* (expt (sec ,arg) 2) ,(derive
>> (if (listp arg) arg (list arg)))))
>>
>> (def-expansion csc/1 (csc 1) (arg) `(* (- (csc ,arg)) (cot ,arg)
>> ,(derive (if (listp arg) arg (list arg)))))
>>
>> (def-expansion sec/1 (sec 1) (arg) `(* (sec ,arg) (tan ,arg) ,(derive
>> (if (listp arg) arg (list arg)))))
>>
>> (def-expansion cot/1 (cot 1) (arg) `(* (- (expt (csc ,arg) 2))
>> ,(derive (if (listp arg) arg (list arg)))))
>>
>> (defun csc (x) "csc -- (csc x) Calculate the cosecant of x" (/ (sin
>> x)))
>>
>> (defun sec (x) "sec -- (sec x) Calculate the secant of x" (/ (cos
>> x)))
>>
>> (defmacro define-equation-functions (name variable equation) (let
>> ((derivative-name (intern (string-upcase (format nil "d/d~a-~a"
>> variable name)))) (derivative (derive equation))) `(progn (defun
>> ,name (,variable) ,equation) (defun ,derivative-name (,variable)
>> ,derivative))))
>>
>> (defmacro take-derivative (equation) (let ((derivative (derive
>> equation))) `',derivative))
>>
>> ;;; End derive

PJB> -- __Pascal Bourguignon__ http://www.informat... â??The
PJB> factory of the future will have only two employees, a man and a
PJB> dog. The man will be there to feed the dog. The dog will be there
PJB> to keep the man from touching the equipment.� -- Carl Bass CEO
PJB> Autodesk

Thanks,

Sam

--
Samuel W. Flint
4096R/266596F4
(9477 D23E 389E 40C5 2F10 DE19 68E5 318E 2665 96F4)
(λs.s s) λs.s s

Pascal J. Bourguignon

10/20/2015 6:32:00 PM

0

swflint@flintfam.org (Samuel W. Flint) writes:

> For some reason (OK, I don't think I've ever used DEFSTRUCT), I didn't
> think to use DEFSTRUCT. I don't really care how it abstracts it away, I
> just used lists because I could debug them easily.

This is a valid reason. Also, you can easily write literal structures
using them.

When you use non-list structure you can use the #S(â?¦) syntax (or the
vector syntax if you use vector structures).

On the other hand, there's no predefined literal syntax for hash-table
or CLOS objects, so indeed, for quick little programs, using lists or
structures is better.


>>> (defun generate-match-expression (on arity &optional (type '=))
>>> (declare (symbol on type) (integer arity))
>
> PJB> Don't use declare it doesn't do what you think. Use check-type:
>
> PJB> (check-type on symbol) (check-type type symbol) (check-type
> PJB> arity (integer 0))
>
> OK. What then is the difference between DECLARE and CHECK-TYPE?

With DECLARE, you tell the compiler that it doesn't need to check the
type, that YOU ensure that the types will be as you declare them.

With CHECK-TYPE, you ask the compiler to CHECK the type and signal a
correctable error if the type is incorrect.

(The worst would be to use both at the same time, since the declare
would render the check-type useless, and the compiler would just remove
it).


--
__Pascal Bourguignon__ http://www.informat...
â??The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.� -- Carl Bass CEO Autodesk

taruss

10/20/2015 6:36:00 PM

0

On Tuesday, October 20, 2015 at 10:01:29 AM UTC-7, Samuel W. Flint wrote:
> >>>>> Pascal J Bourguignon writes:
>
> PJB> swflint@flintfam.org (Samuel W. Flint) writes:
> >> (defun generate-match-expression (on arity &optional (type '=))
> >> (declare (symbol on type) (integer arity))
>
> PJB> Don't use declare it doesn't do what you think. Use check-type:
>
> PJB> (check-type on symbol) (check-type type symbol) (check-type
> PJB> arity (integer 0))
>
> OK. What then is the difference between DECLARE and CHECK-TYPE?

DECLARE is a promise you make to the compiler. Under certain optimization
settings the compiler can use that information to generate more efficient code.
The compiler can also rely on that promise, which could cause your program to
crash (sometimes with a segfault) if you violate that promise. Also, any
implementation is free to completely ignore type declarations. This is why
this isn't perhaps what you think it is. It is one of the ways lisp is
different from many other programming languages.

CHECK-TYPE is a type check that will generate a nice error message if the type
is not correct.

So the difference is that CHECK-TYPE checks the types whereas DECLARE does not
guarantee a type check and can allow the compiler to blindly trust the declared
type. (For some compilers and compiler settings, declarations lead to type
warnings, but this is not required by the standard. Technically, violating the
promise of the declarations leads to undefined behavior.)

So, for example in MCL if you do
(defun add5 (x)
(declare (type x double-float)
(optimize (speed 3) (safety 0) (debug 0)))
(+ x 5.0d0))

Then calling it with (ADD5 3) will crash the program.
You would need to call it with (ADD5 3.0d0) to use it properly.

If instead you did
(defun add7 (x)
(check-type x double-float)
(+ x 7.0d0))

and then called it with (ADD7 3) it will give you a correctable error instead.
It will not silently crash.

Summing up: DECLARE is an optimization. CHECK-TYPE gives you type safety.