[pro] style question about types, coercion, expectations for function parameters

Stelian Ionescu sionescu at cddr.org
Thu May 31 19:45:28 UTC 2012


On Thu, 2012-05-31 at 14:50 -0400, Ryan Davis wrote:
[...]
> I'd like some more opinions on a pattern that has cropped up. One of the
> problems we were having was quickly determining what a function expected
> for it's arguments. As a somewhat contrived example, SLIME helpfully
> would tell me that #'send-email wanted (to from subject body), but then
> it was left to me to guess what values I should pass in.  In real code
> this was frequently non-trivial, and we'd be hand-tracing to figure out
> where the parameter was used to figure out what it should be. Should
> "to" be a string, a CLOS Client object, or the database ID of a Client?
> The answer we arrived at was "yes":
> 
>     (defun send-email (to from subject body)
>       (let ((to (etypecase to
>                   (string to)
>                   ((integer 0) (email (fetch-client to)))
>                   (client (email to))
>                   )))
>         ;; ... more code
>         ))
> 
> The "to" parameter can be anythings that can be mapped to an email
> address. It is send-email's job to send email, and it will figure it out
> based on whatever you provide. If it can't do it, it'll tell you.
> Usually the etypecase is pulled into it's own function, and we have
> something like:
> 
>     (defun send-email (to from subject body)
>       (let ((to (coerce-email to)))
>         ;; ... more code
>         ))

I've started using this idiom too: for a type FOO, have, in addition to
the assembling constructor that is make-instance, a coercing constructor
having the same name as the type itself, which can be elided with a
clever use of inlining and type declarations. The CL standard has some
instances of this, e.g. with pathnames: cl:pathname coerces a
pathname-designator(pathnamem, string or file stream) and
cl:make-pathname assembles from components.

Example:

(declaim (inline email))
(defun email (email-designator)
  (etypecase email-designator
    (string
     email-designator)
    (unsigned-byte
     (email-of (fetch-client email-designator)))
    (client
     (email-of email-designator))))

(declaim (inline send-email))
(defun send-email (to from subject body)
  (let ((to (email to))
        (from (email from)))
    (%send-email to from subject body)))

(defun send-site-warning (to)
  (declare (type string to))
  (send-email to "admin at site.com" "Warning" "Bandwidth quota reached"))

The creation of the above wrapper - send-email that checks type and
coerces then calls %send-email - can also be easily automated with some
macrology

-- 
Stelian Ionescu a.k.a. fe[nl]ix
Quidquid latine dictum sit, altum videtur.
http://common-lisp.net/project/iolib

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: This is a digitally signed message part
URL: <https://mailman.common-lisp.net/pipermail/pro/attachments/20120531/0e45f9f2/attachment.sig>


More information about the pro mailing list