From: Neil Baylis
Subject: CLOS newbie question: defgeneric
Date: 
Message-ID: <5420137a-10e9-459b-9541-ba42664d87b5@u9g2000prd.googlegroups.com>
Let's say I have two classes, point and line. (A point has x & y
coordinates, a line has two points).

I define a method that returns the angle of a line with respect to the
x axis:

(defmethod angle ((l line)) ...)

Now I want to define a method that returns the angle that two points
would make if they defined a line.

(defmethod angle ((p1 point) (p2 point)) ...)

Obviously, these are not congruent methods becasue they differ in the
number of required params.

To make it work, I did the following:

(defgeneric angle (a &optional b))

(defmethod angle ((l line) &optional b)
  (declare (ignore b)) ... )

(defmethod angle ((p1 point) &optional p2) ...)

The problem I have with this is that I end up with misleading
definitions: If I pass in a line, it says there's an optional second
argument, but that argument never does anything. If I pass in a pair
of points, it says that the second point is optional, when in fact it
is mandatory.

Where did I go wrong?

Neil

From: Slobodan Blazeski
Subject: Re: CLOS newbie question: defgeneric
Date: 
Message-ID: <b2fbe84b-1a0f-443f-9a29-8cf65506bb6b@j20g2000vbp.googlegroups.com>
On Jun 20, 7:27 pm, Neil Baylis <···········@gmail.com> wrote:
> Let's say I have two classes, point and line. (A point has x & y
> coordinates, a line has two points).
>
> I define a method that returns the angle of a line with respect to the
> x axis:
>
> (defmethod angle ((l line)) ...)
>
> Now I want to define a method that returns the angle that two points
> would make if they defined a line.
>
> (defmethod angle ((p1 point) (p2 point)) ...)
>
> Obviously, these are not congruent methods becasue they differ in the
> number of required params.
>
> To make it work, I did the following:
>
> (defgeneric angle (a &optional b))
>
> (defmethod angle ((l line) &optional b)
>   (declare (ignore b)) ... )
>
> (defmethod angle ((p1 point) &optional p2) ...)
>
> The problem I have with this is that I end up with misleading
> definitions: If I pass in a line, it says there's an optional second
> argument, but that argument never does anything. If I pass in a pair
> of points, it says that the second point is optional, when in fact it
> is mandatory.
>
> Where did I go wrong?
>
> Neil

Either keep them as separate generic functions or do something  like:
(defconstant x-axis (make-instance 'line ...

(defmethod angle ((x line) (y line) ...
and pass x-axis as parameter, or *curry* it

(defmethod x-axis-angle ((l line))
  (angle l x-axis))



Slobodan
From: Neil Baylis
Subject: Re: CLOS newbie question: defgeneric
Date: 
Message-ID: <e52f15ce-88c5-4ec3-86bb-8d1193592c3d@d7g2000prl.googlegroups.com>
On Jun 20, 11:50 am, Slobodan Blazeski <·················@gmail.com>
wrote:
> On Jun 20, 7:27 pm, Neil Baylis <···········@gmail.com> wrote:
>
>
>
>
>
> > Let's say I have two classes, point and line. (A point has x & y
> > coordinates, a line has two points).
>
> > I define a method that returns the angle of a line with respect to the
> > x axis:
>
> > (defmethod angle ((l line)) ...)
>
> > Now I want to define a method that returns the angle that two points
> > would make if they defined a line.
>
> > (defmethod angle ((p1 point) (p2 point)) ...)
>
> > Obviously, these are not congruent methods becasue they differ in the
> > number of required params.
>
> > To make it work, I did the following:
>
> > (defgeneric angle (a &optional b))
>
> > (defmethod angle ((l line) &optional b)
> >   (declare (ignore b)) ... )
>
> > (defmethod angle ((p1 point) &optional p2) ...)
>
> > The problem I have with this is that I end up with misleading
> > definitions: If I pass in a line, it says there's an optional second
> > argument, but that argument never does anything. If I pass in a pair
> > of points, it says that the second point is optional, when in fact it
> > is mandatory.
>
> > Where did I go wrong?
>
> > Neil
>
> Either keep them as separate generic functions or do something  like:
> (defconstant x-axis (make-instance 'line ...
>
> (defmethod angle ((x line) (y line) ...
> and pass x-axis as parameter, or *curry* it
>
> (defmethod x-axis-angle ((l line))
>   (angle l x-axis))
>
> Slobodan

Maybe I should just say that angle takes exactly one argument. If I
need to find the angle of some points, I'll pass them as a list and
specialize the method for lists.

Neil
From: Slobodan Blazeski
Subject: Re: CLOS newbie question: defgeneric
Date: 
Message-ID: <cc670718-9cdd-4cf7-a330-fc291d94a9c0@r16g2000vbn.googlegroups.com>
On Jun 20, 9:37 pm, Neil Baylis <···········@gmail.com> wrote:
>
> Maybe I should just say that angle takes exactly one argument. If I
> need to find the angle of some points, I'll pass them as a list and
> specialize the method for lists.
>
> Neil

You're free to do whatever fits your model of thinking  best, though I
don't think specializing on list gives any clue what kind of element
that list should contain . Like what prevents me to call it like this:
(angle '(1 2 3 4))

You can also pass two point as line if you want to go with one
argument:
(defmethod angle ((x line))..

(angle some-line) ; if you already have one
(angle (make-line point1 point2)) ; if you have two points so you
contruct line on the fly:
with make-line being
(defmethod make-line ((p1 point) (p2 point))
   (make-instance 'line :point1 p1 :point2 p2))

It's up to you and if you change your mind later you can rewrite it.

Slobodan
From: Michael Weber
Subject: Re: CLOS newbie question: defgeneric
Date: 
Message-ID: <PM00046CCB398AACAC@roadkill.lan>
Neil Baylis wrote:
> Where did I go wrong?

In unifying two GFs when they should perhaps be separate:

(defgeneric line-angle (line))

(defgeneric points-angle (point-1 point-2))
From: Pascal Costanza
Subject: Re: CLOS newbie question: defgeneric
Date: 
Message-ID: <7a510oF1tofqjU1@mid.individual.net>
Neil Baylis wrote:
> Let's say I have two classes, point and line. (A point has x & y
> coordinates, a line has two points).
> 
> I define a method that returns the angle of a line with respect to the
> x axis:
> 
> (defmethod angle ((l line)) ...)
> 
> Now I want to define a method that returns the angle that two points
> would make if they defined a line.
> 
> (defmethod angle ((p1 point) (p2 point)) ...)
> 
> Obviously, these are not congruent methods becasue they differ in the
> number of required params.
> 
> To make it work, I did the following:
> 
> (defgeneric angle (a &optional b))
> 
> (defmethod angle ((l line) &optional b)
>   (declare (ignore b)) ... )
> 
> (defmethod angle ((p1 point) &optional p2) ...)
> 
> The problem I have with this is that I end up with misleading
> definitions: If I pass in a line, it says there's an optional second
> argument, but that argument never does anything. If I pass in a pair
> of points, it says that the second point is optional, when in fact it
> is mandatory.

Provide an 'internal' method that has all of these arguments as required 
ones. Ensure that the second argument is nil in the line case:

(defmethod %angle ((p1 point) (p2 point)) ...)
(defmethod %angle ((l line) (ignore (eql nil)) ...)

Now put a utility function on top that calls this one as needed:

(declaim (inline angle))

(defun angle (arg1 &optional arg2)
   (%angle arg1 arg2))


Not ideal, but maybe it helps.


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Björn Lindberg
Subject: Re: CLOS newbie question: defgeneric
Date: 
Message-ID: <9mpocsgxwm4.fsf@muvclx01.cadence.com>
Neil Baylis <···········@gmail.com> writes:

> Let's say I have two classes, point and line. (A point has x & y
> coordinates, a line has two points).
>
> I define a method that returns the angle of a line with respect to the
> x axis:
>
> (defmethod angle ((l line)) ...)
>
> Now I want to define a method that returns the angle that two points
> would make if they defined a line.
>
> (defmethod angle ((p1 point) (p2 point)) ...)
>
> Obviously, these are not congruent methods becasue they differ in the
> number of required params.
>
> To make it work, I did the following:
>
> (defgeneric angle (a &optional b))
>
> (defmethod angle ((l line) &optional b)
>   (declare (ignore b)) ... )
>
> (defmethod angle ((p1 point) &optional p2) ...)
>
> The problem I have with this is that I end up with misleading
> definitions: If I pass in a line, it says there's an optional second
> argument, but that argument never does anything. If I pass in a pair
> of points, it says that the second point is optional, when in fact it
> is mandatory.
>
> Where did I go wrong?

From a design perspective, start by writing the function documentation
for the generic function.

Alternative I:

  Returns the angle between the first argument and the x axis.

Alternative II:

  Returns the angle between the first argument and the x axis, except
  if the first argument is a point, in which case there must be a
  second argument which is also a point, and the angle between the
  line between these points and the x axis is returned.

A good design principle for generic functions is that they perform
conceptually the same operation, but on arguments of varying
types. The purpose of it is to hide the implementational differences
of performing the conceptually same operation on objects of varying
types. With this in mind, if you have to mention the behaviour for
different types of arguments in the function documentation it is a
hint that maybe the operations do not belong together as methods of
the same generic function.

Another design principle is to use generic functions for run time
polymorphism. In your case, there is no run time polymorphism
involved. At each and every call site in the source code it is clearly
visible whether the line method or the point method is called. Using a
generic function here is akin to writing (when (< 1 2) ...). Your
program is making a run time decision, but always comes to the same
conclusion.

You could define a separate function for the points case, which is the
outlier here (what is the angle between two points?). Alternatively
you might be able to construct a line out of the points for the
purpose of finding the angle: (angle (make-line p1 p2)).


Bj�rn Lindberg
From: Neil Baylis
Subject: Re: CLOS newbie question: defgeneric
Date: 
Message-ID: <668c0703-3d66-48e0-8458-13a46610364a@x31g2000prc.googlegroups.com>
On Jun 22, 5:48 am, ·····@runa.se (Björn Lindberg) wrote:
> Neil Baylis <···········@gmail.com> writes:
> > Let's say I have two classes, point and line. (A point has x & y
> > coordinates, a line has two points).
>
> > I define a method that returns the angle of a line with respect to the
> > x axis:
>
> > (defmethod angle ((l line)) ...)
>
> > Now I want to define a method that returns the angle that two points
> > would make if they defined a line.
>
> > (defmethod angle ((p1 point) (p2 point)) ...)
>
> > Obviously, these are not congruent methods becasue they differ in the
> > number of required params.
>
> > To make it work, I did the following:
>
> > (defgeneric angle (a &optional b))
>
> > (defmethod angle ((l line) &optional b)
> >   (declare (ignore b)) ... )
>
> > (defmethod angle ((p1 point) &optional p2) ...)
>
> > The problem I have with this is that I end up with misleading
> > definitions: If I pass in a line, it says there's an optional second
> > argument, but that argument never does anything. If I pass in a pair
> > of points, it says that the second point is optional, when in fact it
> > is mandatory.
>
> > Where did I go wrong?
>
> From a design perspective, start by writing the function documentation
> for the generic function.
>
> Alternative I:
>
>   Returns the angle between the first argument and the x axis.
>
> Alternative II:
>
>   Returns the angle between the first argument and the x axis, except
>   if the first argument is a point, in which case there must be a
>   second argument which is also a point, and the angle between the
>   line between these points and the x axis is returned.
>
> A good design principle for generic functions is that they perform
> conceptually the same operation, but on arguments of varying
> types. The purpose of it is to hide the implementational differences
> of performing the conceptually same operation on objects of varying
> types. With this in mind, if you have to mention the behaviour for
> different types of arguments in the function documentation it is a
> hint that maybe the operations do not belong together as methods of
> the same generic function.
>
> Another design principle is to use generic functions for run time
> polymorphism. In your case, there is no run time polymorphism
> involved. At each and every call site in the source code it is clearly
> visible whether the line method or the point method is called. Using a
> generic function here is akin to writing (when (< 1 2) ...). Your
> program is making a run time decision, but always comes to the same
> conclusion.
>
> You could define a separate function for the points case, which is the
> outlier here (what is the angle between two points?). Alternatively
> you might be able to construct a line out of the points for the
> purpose of finding the angle: (angle (make-line p1 p2)).
>
> Björn Lindberg

Thanks, that's helpful. I think that at the call site it will be easy
to create a line between the two points and get the angle of that line
as you suggested.

I had thought that run-time polymorphism was exactly what I was trying
to accomplish. In this application, I want the user to be able to say
"make a triangle here at the same angle as that square over there". To
specify the angle of the new triangle, the user would simply select
the existing square (or perhaps one of its edges).

Currently, I have (angle ..) defined for a single point, and a single
line. If I make an instance of either at runtime, I can pass that
instance to (angle ..) and the correct method is called. Isn't that
runtime polymorphism?

Neil Baylis
From: Björn Lindberg
Subject: Re: CLOS newbie question: defgeneric
Date: 
Message-ID: <m2hby8rp3a.fsf@nex.nex>
Neil Baylis <···········@gmail.com> writes:

> I had thought that run-time polymorphism was exactly what I was trying
> to accomplish. In this application, I want the user to be able to say
> "make a triangle here at the same angle as that square over there". To
> specify the angle of the new triangle, the user would simply select
> the existing square (or perhaps one of its edges).
>
> Currently, I have (angle ..) defined for a single point, and a single
> line. If I make an instance of either at runtime, I can pass that
> instance to (angle ..) and the correct method is called. Isn't that
> runtime polymorphism?

Yes it is. What is not polymorphism is what you had in your original
post, where one of the methods demanded two arguments, and the other was
content with only one. At every call site you can then see which method
will be invoked by how many arguments are provided.


Bj�rn Lindberg
From: Neil Baylis
Subject: Re: CLOS newbie question: defgeneric
Date: 
Message-ID: <5cb39de2-2b27-45e6-b504-a385995ad980@r37g2000yqd.googlegroups.com>
On Jun 22, 1:27 pm, ·····@runa.se (Björn Lindberg) wrote:
> Neil Baylis <···········@gmail.com> writes:
> > I had thought that run-time polymorphism was exactly what I was trying
> > to accomplish. In this application, I want the user to be able to say
> > "make a triangle here at the same angle as that square over there". To
> > specify the angle of the new triangle, the user would simply select
> > the existing square (or perhaps one of its edges).
>
> > Currently, I have (angle ..) defined for a single point, and a single
> > line. If I make an instance of either at runtime, I can pass that
> > instance to (angle ..) and the correct method is called. Isn't that
> > runtime polymorphism?
>
> Yes it is. What is not polymorphism is what you had in your original
> post, where one of the methods demanded two arguments, and the other was
> content with only one. At every call site you can then see which method
> will be invoked by how many arguments are provided.
>
> Björn Lindberg

OK, thanks, I understand.

Neil Baylis