From: Barry Margolin
Subject: Re: defstruct with :constructor
Date: 
Message-ID: <__AU.27$aU.446989@cam-news-reader1.bbnplanet.com>
In article <··············@mute.eaglets.com>,
Sam Steingold  <···@usa.net> wrote:
>I would like to be able to write:
>(defstruct (triangle :constructor make-triangle)
> ...)
>
>(defun make-triangle (&key (side1 0.0) (side2 0.0) (side3 0.0)
>		      (area (triangle-area side1 side2 side3)))
>  (make-triangle-system :side1 side1  :side2 side2  :side3 side3 :area :area))

(defstruct (triangle :constructur make-triangle-internal)
  ...)

(defun make-triangle (&key (side1 0.0) (side2 0.0) (side3 0.0)
		      (area (compute-triangle-area side1 side2 side3)))
  (make-triangle-internal :side1 side1  :side2 side2  :side3 side3 :area :area))

(defun compute-triangle-area (s1 s2 s3)
  ...)

Note that the name of the function used to compute the area is *not*
TRIANGLE-AREA.  That's the name of the accessor for the AREA slot that will
be defined automatically by DEFSTRUCT, so you need a different name for the
computation function (well, you *could* use the :CONC-NAME option to
specify a different naming scheme for the accessors, but I think that would
be more confusing to users, and COMPUTE-TRIANGLE-AREA is better for this
function anyway).

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Cambridge, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.

From: Barry Margolin
Subject: Re: defstruct with :constructor
Date: 
Message-ID: <a4QU.11$G%.326390@cam-news-reader1.bbnplanet.com>
In article <··············@mute.eaglets.com>,
Sam Steingold  <···@usa.net> wrote:
>I get an error: function make-triangle-internal is not defined.

Sorry, I made the same syntax error you did (not to mention a misspelling).  It should be:

(defstruct (triangle (:constructor make-triangle-internal)) ...)

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Cambridge, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
From: Steve Gonedes
Subject: Re: defstruct with :constructor
Date: 
Message-ID: <6g2fsn$bt0@bgtnsc02.worldnet.att.net>
Sam Steingold <···@usa.net> writes:

< Another trouble is that I will be specifying the default values for
< sides twice - in my (defstruct triangle) and (defun make-triangle).
< 
< What about permitting the init expressions defstruct to refer to the
< slots themselves, so that they are evaluated recursively, as labels?



You could do this, but &aux is evil, unnecessary, and maybe even
misleading (I think I used it wrong anyway). Just thought you might
like to play with it.

(defstruct (triangle
            (:constructor make-triangle
                          (&key (side1 0.0) (side2 0.0) (side3 0.0)
                           &aux (assert (every #'numberp
                                               (list side1 side2 side3))))))
  side1 side2 side3
  (area (+ side1 side2 side3)))


You could always do something like this, or use an auxillery function
as Barry suggested.


(defun assert-all (fn &rest args)
  (if (every fn args) t
      (error "Insert error message here...")))

(defun triangle-make-area (a b c)
  (assert-all #'numberp a b c)
  (* (+ a c) 1/2))

(defstruct (triangle
            (:constructor make-triangle
                          (&key (side1 0.0) (side2 0.0) (side3 0.0))))
  side1 side2 side3
  (area (triangle-make-area side1 side2 side3)))



Actually, I would probably just use this. It's usually pretty easy to
spot an invalid type I think. I always hate when the majority of the
code is checking for something to be wrong.


(defstruct (triangle
            (:constructor make-triangle
                          (&key (side1 0.0) (side2 0.0) (side3 0.0))))
  (side1 :type double-float)
  (side2 :type double-float)
  (side3 :type double-float)
  (area (* (+ side1 side3) 1/2)))



USER(93): (make-triangle :side1 #\r)

Error: EXCL::+_2OP: `#\r' is not of the expected type `NUMBER'
  [condition type: TYPE-ERROR]
[1] USER(94): :current
(+ #\r 0.0)

[1] USER(95): :zoom
Evaluation stack:

   (ERROR TYPE-ERROR :DATUM ...)
 ->(+ #\r 0.0)
   (MAKE-TRIANGLE :SIDE1 #\r)
   (EVAL (MAKE-TRIANGLE :SIDE1 #))
   (TPL:TOP-LEVEL-READ-EVAL-PRINT-LOOP)
   (TPL:START-INTERACTIVE-TOP-LEVEL
      #<BIDIRECTIONAL-TERMINAL-STREAM [initial terminal io] fd 0/1 @
        #x811794a>
      #<Function TOP-LEVEL-READ-EVAL-PRINT-LOOP> ...)
[1] USER(96): 


Seems like the hard is finding out who called make-triangle (well not
in this case). You should get the general idea. There's a bug in the
latter so you know; doesn't check type of :side2, but I figure, if you
put the declarations there, why check for invalid types? have fun.
From: Barry Margolin
Subject: Re: defstruct with :constructor
Date: 
Message-ID: <Q5XU.25$G%.579149@cam-news-reader1.bbnplanet.com>
In article <··············@mute.eaglets.com>,
Sam Steingold  <···@usa.net> wrote:
>Specifically: there are cases when make-triangle-internal will be called
>by the system, and a triangle with bad area will be created.  The only
>example I can think of is
>	(read-from-string "#S(triangle side1 1 side2 1 side3 1)")
>this is highly unfortunate, but, apparently, there is no way out.

True.  But normally, #S is used to read the printed representation of a
structure that was created by another call, so it will have its area filled
in.

Your best bet is to use the lazy technique that Kent suggested, where you
compute the area the first time you need it and cache it in the area slot.

>Another trouble is that I will be specifying the default values for
>sides twice - in my (defstruct triangle) and (defun make-triangle).

Don't bother specifying them in the (defstruct triangle), since everyone
should be calling make-triangle.

>What about permitting the init expressions defstruct to refer to the
>slots themselves, so that they are evaluated recursively, as labels?

Sorry, not available.  You can do that if you define a :constructor with a
fancy lambda list.

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Cambridge, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.