From: Albert Krewinkel
Subject: Implementing intervals - design question
Date: 
Message-ID: <fgk7iq$t7e$1@registered.motzarella.org>
Hi,

I could use some help on the following design question I face: I want to
implement intervals which should look somewhat like this:

(defclass simple-interval ()
  ((lower-bound :accessor lower-bound :initarg :lower-bound)
   (upper-bound :accessor upper-bound :initarg :upper-bound)
   (lower-bound-included-p :accessor lower-bound-included-p
                           :initarg :lower-bound-included-p)
   (upper-bound-included-p :accessor upper-bound-included-p
                           :initarg :upper-bound-included-p)
   (superset :accessor superset :initarg :superset)))

Now I have some other class

(defclass foo () ((foo :initarg :foo) (bar :initarg :bar)))

and want to use it as an interval.

I see two obvious solutions to this:

- Using a macro `definterval' to define `foo' and create methods for
  lower-bound and upper-bound this way.

- Defining a metaobject class `interval' and add autogenerated reader
  methods.

To me it seems like it is basicaly the same thing.  But then, I don't
have much experience yet in using the MOP, so I could be missing some
important points here.  Having somebody explain the differences would
help me getting a better understanding of the issue.

Thanks in advance

Albert

From: Rainer Joswig
Subject: Re: Implementing intervals - design question
Date: 
Message-ID: <joswig-903CBD.12092804112007@news-europe.giganews.com>
In article <············@registered.motzarella.org>,
 Albert Krewinkel <·······@KEIN-SPAMnatwiss.uni-luebeck.de> wrote:

> Hi,
> 
> I could use some help on the following design question I face: I want to
> implement intervals which should look somewhat like this:
> 
> (defclass simple-interval ()
>   ((lower-bound :accessor lower-bound :initarg :lower-bound)
>    (upper-bound :accessor upper-bound :initarg :upper-bound)
>    (lower-bound-included-p :accessor lower-bound-included-p
>                            :initarg :lower-bound-included-p)
>    (upper-bound-included-p :accessor upper-bound-included-p
>                            :initarg :upper-bound-included-p)
>    (superset :accessor superset :initarg :superset)))
> 
> Now I have some other class
> 
> (defclass foo () ((foo :initarg :foo) (bar :initarg :bar)))
> 
> and want to use it as an interval.

What does 'use' mean?

* You want to use objects of class foo similar to intervals?
  Just define the methods for it. Done.

* You want to inherit methods and slots from an Interval
  class?
  Define a superclass and inherit from there.

(defclass interval ()
  ( ...))

You will define a protocol: what classes, slots and methods
should there be. The implementation of those then
is done in subclasses interval and in the methods
you write for those.

(defclass simple-interval (interval)
  (...))

(defclass foo (interval)
   ...)

(defmethod lower-bound ((object foo))
  ...)

and so on.

For more background how to use CLOS I would recommend
reading AMOP (Art of the Metaobject Protocol) - not
for its treatment of the MOP, but to see how they
use CLOS.

You can also look at the CLIM spec - there you can
see how these APIs may look like.

> 
> I see two obvious solutions to this:
> 
> - Using a macro `definterval' to define `foo' and create methods for
>   lower-bound and upper-bound this way.
> 
> - Defining a metaobject class `interval' and add autogenerated reader
>   methods.
> 
> To me it seems like it is basicaly the same thing.  But then, I don't
> have much experience yet in using the MOP, so I could be missing some
> important points here.  Having somebody explain the differences would
> help me getting a better understanding of the issue.
> 
> Thanks in advance
> 
> Albert
From: Albert Krewinkel
Subject: Re: Implementing intervals - design question
Date: 
Message-ID: <fgkbjh$bpi$1@registered.motzarella.org>
On Sun, 04 Nov 2007 12:09:28 +0100, Rainer Joswig wrote:
> What does 'use' mean?

You are right, I wasn't precise in my question and should have taken
more time to phrase what I mean. Sorry for letting you do guesswork.

> * You want to use objects of class foo similar to intervals?
>   Just define the methods for it. Done.
> 
> * You want to inherit methods and slots from an Interval
>   class?
>   Define a superclass and inherit from there.
> 
> (defclass interval ()
>   ( ...))
> 
> You will define a protocol: what classes, slots and methods
> should there be. The implementation of those then
> is done in subclasses interval and in the methods
> you write for those.
> 
> (defclass simple-interval (interval)
>   (...))
> 
> (defclass foo (interval)
>    ...)
> 
> (defmethod lower-bound ((object foo))
>   ...)
> 
> and so on.

That is what I tried to say with 
>> - Using a macro `definterval' to define `foo' and create methods for
>>   lower-bound and upper-bound this way.

So using a macro _can_ be the next step, but defining a protocol
should be the first thing to do? All right, thanks for making that
clear.

> For more background how to use CLOS I would recommend
> reading AMOP (Art of the Metaobject Protocol) - not
> for its treatment of the MOP, but to see how they
> use CLOS.
> 
> You can also look at the CLIM spec - there you can
> see how these APIs may look like.

I will do that. Thanks a lot for your help!

Albert
From: Alex Mizrahi
Subject: Re: Implementing intervals - design question
Date: 
Message-ID: <472db55f$0$90268$14726298@news.sunsite.dk>
 AK> I could use some help on the following design question I face: I want 
to
 AK> implement intervals which should look somewhat like this:

 AK> (defclass simple-interval ()
 AK>   ((lower-bound :accessor lower-bound :initarg :lower-bound)
 AK>    (upper-bound :accessor upper-bound :initarg :upper-bound)
 AK>    (lower-bound-included-p :accessor lower-bound-included-p
 AK>                            :initarg :lower-bound-included-p)
 AK>    (upper-bound-included-p :accessor upper-bound-included-p
 AK>                            :initarg :upper-bound-included-p)
 AK>    (superset :accessor superset :initarg :superset)))

i can't imagine reason why can't you implement

(defmethod lower-bound-included-p ((si simple-interval)) (slot-boundp si 
'lower-bound))

that is, you don't actually need lower-bound-included-p slot, and in case 
NIL is not legal value for lower-bound, you can just use it to indicate it's 
not included..

 AK> I see two obvious solutions to this:

 AK> - Using a macro `definterval' to define `foo' and create methods for
 AK>   lower-bound and upper-bound this way.

 AK> - Defining a metaobject class `interval' and add autogenerated reader
 AK>   methods.

this looks for me like an over-engineering attempt.. what's wrong with:

(defmethod lower-bound ((f foo)) (frob (foo f) (bar f)))

idea of CLOS is that methods do not belong to classes. you implement 
interval API implementing methods for generic functions of interval API --  
and that's all. you don't need any base classes, macros or MOP to do this.

if you absolutely would like to make some macros, you can do macro 
IMPLEMENT-INTERVAL-API to automatically define methods (if they have 
something in common), but i see no reason for mutilating defclass..
From: Albert Krewinkel
Subject: Re: Implementing intervals - design question
Date: 
Message-ID: <fgketv$nt7$1@registered.motzarella.org>
On Sun, 04 Nov 2007 14:04:44 +0200, Alex Mizrahi wrote:
> AK> I could use some help on the following design question I face: I want 
> to
>  AK> implement intervals which should look somewhat like this:
> 
>  AK> (defclass simple-interval ()
>  AK>   ((lower-bound :accessor lower-bound :initarg :lower-bound)
>  AK>    (upper-bound :accessor upper-bound :initarg :upper-bound)
>  AK>    (lower-bound-included-p :accessor lower-bound-included-p
>  AK>                            :initarg :lower-bound-included-p)
>  AK>    (upper-bound-included-p :accessor upper-bound-included-p
>  AK>                            :initarg :upper-bound-included-p)
>  AK>    (superset :accessor superset :initarg :superset)))
> 
> i can't imagine reason why can't you implement
> 
> (defmethod lower-bound-included-p ((si simple-interval)) (slot-boundp si 
> 'lower-bound))
> 
> that is, you don't actually need lower-bound-included-p slot, and in case 
> NIL is not legal value for lower-bound, you can just use it to indicate it's 
> not included..

I want be able to use intervals in a mathematical sense, so I can do
the following:

(defparameter *interval1*
  (make-instance 'simple-interval :lower-bound 0 :upper-bound 100
                 :upper-bound-included-p nil))
=> #<SIMPLE-INTERVAL [0,100[>

(defparameter *interval2*
  (make-instance 'simple-interval :lower-bound 23 :upper-bound 42))
=> #<SIMPLE-INTERVAL [23,42]>

;; Get the complement of *interval2* in the set defined by *interval1*
(interval-complement *interval2* *interval1*)
=> #<MULTI-INTERVAL (#<SIMPLE-INTERVAL [0,23[> #<SIMPLE-INTERVAL ]42,100[> )>

That's also going to come in handy when I will use intervals containing
only integers, so
(interval-intersection [23,42]  ]34,50] :result-type 'integer-interval)
will return [23,33].

Naturaly, the *-bound-included-p slots are only necessary for
intervals defined in dense sets, so yes, I can drop them in most
classes.

>  AK> I see two obvious solutions to this:
> 
>  AK> - Using a macro `definterval' to define `foo' and create methods for
>  AK>   lower-bound and upper-bound this way.
> 
>  AK> - Defining a metaobject class `interval' and add autogenerated reader
>  AK>   methods.
> 
> this looks for me like an over-engineering attempt.. what's wrong with:
> 
> (defmethod lower-bound ((f foo)) (frob (foo f) (bar f)))
> 
> idea of CLOS is that methods do not belong to classes. you implement 
> interval API implementing methods for generic functions of interval API --  
> and that's all. you don't need any base classes, macros or MOP to do this.

Yep, but having a common interval superclass still can be usefull
sometimes (just in case I need a (typep x interval), for example).

> if you absolutely would like to make some macros, you can do macro 
> IMPLEMENT-INTERVAL-API to automatically define methods (if they have 
> something in common), but i see no reason for mutilating defclass..

That's good to know. So since there is a simple solution, I can
refrain from using the MOP just now. Thanks for the help
 Albert
From: Marco Antoniotti
Subject: Re: Implementing intervals - design question
Date: 
Message-ID: <1194201544.499859.154310@k79g2000hse.googlegroups.com>
Hi

Apart from the representation/implementation issues you raise, you
should be aware that you do not have portable control over rounding of
floating point operations.  This will eventually hit you once you try
to deal with actual interval operations.  You should check exaclty how
your implementation(s) is(are) dealing with rounding.

Cheers

Marco



On Nov 4, 2:45 pm, Albert Krewinkel <·······@KEIN-SPAMnatwiss.uni-
luebeck.de> wrote:
> On Sun, 04 Nov 2007 14:04:44 +0200, Alex Mizrahi wrote:
> > AK> I could use some help on the following design question I face: I want
> > to
> >  AK> implement intervals which should look somewhat like this:
>
> >  AK> (defclass simple-interval ()
> >  AK>   ((lower-bound :accessor lower-bound :initarg :lower-bound)
> >  AK>    (upper-bound :accessor upper-bound :initarg :upper-bound)
> >  AK>    (lower-bound-included-p :accessor lower-bound-included-p
> >  AK>                            :initarg :lower-bound-included-p)
> >  AK>    (upper-bound-included-p :accessor upper-bound-included-p
> >  AK>                            :initarg :upper-bound-included-p)
> >  AK>    (superset :accessor superset :initarg :superset)))
>
> > i can't imagine reason why can't you implement
>
> > (defmethod lower-bound-included-p ((si simple-interval)) (slot-boundp si
> > 'lower-bound))
>
> > that is, you don't actually need lower-bound-included-p slot, and in case
> > NIL is not legal value for lower-bound, you can just use it to indicate it's
> > not included..
>
> I want be able to use intervals in a mathematical sense, so I can do
> the following:
>
> (defparameter *interval1*
>   (make-instance 'simple-interval :lower-bound 0 :upper-bound 100
>                  :upper-bound-included-p nil))
> => #<SIMPLE-INTERVAL [0,100[>
>
> (defparameter *interval2*
>   (make-instance 'simple-interval :lower-bound 23 :upper-bound 42))
> => #<SIMPLE-INTERVAL [23,42]>
>
> ;; Get the complement of *interval2* in the set defined by *interval1*
> (interval-complement *interval2* *interval1*)
> => #<MULTI-INTERVAL (#<SIMPLE-INTERVAL [0,23[> #<SIMPLE-INTERVAL ]42,100[> )>
>
> That's also going to come in handy when I will use intervals containing
> only integers, so
> (interval-intersection [23,42]  ]34,50] :result-type 'integer-interval)
> will return [23,33].
>
> Naturaly, the *-bound-included-p slots are only necessary for
> intervals defined in dense sets, so yes, I can drop them in most
> classes.
>
> >  AK> I see two obvious solutions to this:
>
> >  AK> - Using a macro `definterval' to define `foo' and create methods for
> >  AK>   lower-bound and upper-bound this way.
>
> >  AK> - Defining a metaobject class `interval' and add autogenerated reader
> >  AK>   methods.
>
> > this looks for me like an over-engineering attempt.. what's wrong with:
>
> > (defmethod lower-bound ((f foo)) (frob (foo f) (bar f)))
>
> > idea of CLOS is that methods do not belong to classes. you implement
> > interval API implementing methods for generic functions of interval API --  
> > and that's all. you don't need any base classes, macros or MOP to do this.
>
> Yep, but having a common interval superclass still can be usefull
> sometimes (just in case I need a (typep x interval), for example).
>
> > if you absolutely would like to make some macros, you can do macro
> > IMPLEMENT-INTERVAL-API to automatically define methods (if they have
> > something in common), but i see no reason for mutilating defclass..
>
> That's good to know. So since there is a simple solution, I can
> refrain from using the MOP just now. Thanks for the help
>  Albert
From: Alan Crowe
Subject: Re: Implementing intervals - design question
Date: 
Message-ID: <86ejf4v5p0.fsf@cawtech.freeserve.co.uk>
Albert Krewinkel <·······@KEIN-SPAMnatwiss.uni-luebeck.de> writes:

> Hi,
> 
> I could use some help on the following design question I face: I want to
> implement intervals which should look somewhat like this:
> 
> (defclass simple-interval ()
>   ((lower-bound :accessor lower-bound :initarg :lower-bound)
>    (upper-bound :accessor upper-bound :initarg :upper-bound)
>    (lower-bound-included-p :accessor lower-bound-included-p
>                            :initarg :lower-bound-included-p)
>    (upper-bound-included-p :accessor upper-bound-included-p
>                            :initarg :upper-bound-included-p)
>    (superset :accessor superset :initarg :superset)))

It struck me that there are two pairs here

lower-bound & lower-bound-included-p

upper-bound & upper-bound-included-p

and you could end up duplicating code that does the same for
each pair. I've doodled with a different design

;;; An interval has two boundaries
;;; which may be inclusive of exclusive
;;; We could have four slots, with two numbers
;;; and two flags, but both number/flag pairs
;;; work the same way, so we prefer to package
;;; the boundary/flag pairs as a boundary-number
;;; and delegate to them 
(defclass interval ()
  ((lower :accessor lower-number
          :initarg :lower)
   (upper :accessor upper-number
          :initarg :upper)))

(defclass boundary-number ()
  ((value :accessor boundary-value
          :initarg :value
          :type number)
   (boundary :accessor included
             :initarg :in
             :type boolean)))

(defun add (&rest args)
  (reduce #'add2 args))

(defmethod add2 ((x boundary-number)
                 (y boundary-number))
  (make-instance 'boundary-number
                 :value (+ (boundary-value x)
                           (boundary-value y))
                 :in (and (included x)
                          (included y))))

(defmethod add2 ((i interval)
                 (j interval))
  (make-instance 'interval
                 :lower (add2 (lower-number i)
                              (lower-number j))
                 :upper (add2 (upper-number i)
                              (upper-number j))))

;;; We don't want to be stuck creating intervals with
;;; nested make-instance's creating boundary-numbers with-in
;;; the call that makes the interval
;;; So we add some more keywords to make-instance 'interval
(defmethod shared-initialize :after
    ((new interval) slots &key from to closed-below closed-above)
  (when (and from to)
    (setf (lower-number new)
          (make-instance 'boundary-number
                         :value from
                         :in closed-below)
          (upper-number new)
          (make-instance 'boundary-number
                         :value to
                         :in closed-above))))

;;; We would like to be able to see what we are doing,
;;; at least when pretty-printing
(set-pprint-dispatch 'interval
  (lambda(stream object)
    (format stream "~:[(~;[~]~A,~A~:[)~;]~]"
            (included (lower-number object))
            (boundary-value (lower-number object))
            (boundary-value (upper-number object))
            (included (upper-number object)))))

;;; make #i(0,1] a half-open interval
;;; which will not work if #\] is a constituent character
(defparameter *terminal-]*
  (copy-readtable))

(set-syntax-from-char #\] #\) *terminal-]*)

;;; the #i readmacro checks thoroughly
(set-dispatch-macro-character #\# #\i
  (lambda(stream sub-character infix-parameter)
    (declare (ignore sub-character infix-parameter))
    (let ((left-bound (read-char stream))
          (left-value (read stream t nil t))
          (middle (read-char stream))
          (right-value (let ((*readtable* *terminal-]*))
                         (read stream t nil t)))
          (right-bound (read-char stream)))
      (check-type left-bound (member #\( #\[))
      (check-type left-value real)
      (check-type middle (eql #\,)
                  "a comma.")
      (check-type right-value real)
      (check-type right-bound (member #\) #\]))
      (make-instance 'interval
                     :from left-value
                     :to right-value
                     :closed-below (char= left-bound #\[)
                     :closed-above (char= right-bound #\])))))

CL-USER> (dolist (x '(#i[0,1]
                      #i(1,2]
                      #i[2,3)
                      #i(3,4)))
           (dolist (y '(#i[0.0,0.1]
                      #i(0.1,0.2]
                      #i[0.2,0.3)
                      #i(0.3,0.4)))
             (format t "~&~S + ~S = ~S"
                     x y (add x y))))
[0,1] + [0.0,0.1] = [0.0,1.1]
[0,1] + (0.1,0.2] = (0.1,1.2]
[0,1] + [0.2,0.3) = [0.2,1.3)
[0,1] + (0.3,0.4) = (0.3,1.4)
(1,2] + [0.0,0.1] = (1.0,2.1]
(1,2] + (0.1,0.2] = (1.1,2.2]
(1,2] + [0.2,0.3) = (1.2,2.3)
(1,2] + (0.3,0.4) = (1.3,2.4)
[2,3) + [0.0,0.1] = [2.0,3.1)
[2,3) + (0.1,0.2] = (2.1,3.2)
[2,3) + [0.2,0.3) = [2.2,3.3)
[2,3) + (0.3,0.4) = (2.3,3.4)
(3,4) + [0.0,0.1] = (3.0,4.1)
(3,4) + (0.1,0.2] = (3.1,4.2)
(3,4) + [0.2,0.3) = (3.2,4.3)
(3,4) + (0.3,0.4) = (3.3,4.4)

That seems to be viable.

Alan Crowe
Edinburgh
Scotland
From: Albert Krewinkel
Subject: Re: Implementing intervals - design question
Date: 
Message-ID: <fgnv07$seg$1@registered.motzarella.org>
On Mon, 05 Nov 2007 19:43:39 +0000, Alan Crowe wrote:
> Albert Krewinkel writes:
> 
>> Hi,
>> 
>> I could use some help on the following design question I face: I want to
>> implement intervals which should look somewhat like this:
>> 
>> (defclass simple-interval ()
>>   ((lower-bound :accessor lower-bound :initarg :lower-bound)
>>    (upper-bound :accessor upper-bound :initarg :upper-bound)
>>    (lower-bound-included-p :accessor lower-bound-included-p
>>                            :initarg :lower-bound-included-p)
>>    (upper-bound-included-p :accessor upper-bound-included-p
>>                            :initarg :upper-bound-included-p)
>>    (superset :accessor superset :initarg :superset)))
> 
> It struck me that there are two pairs here
> 
> lower-bound & lower-bound-included-p
> 
> upper-bound & upper-bound-included-p
> 
> and you could end up duplicating code that does the same for
> each pair. I've doodled with a different design
>
> [snip]
>
> That seems to be viable.
> 
> Alan Crowe
> Edinburgh
> Scotland

The fact that my class layout would result in duplication code became
clear to me too, when I started to implement things.  So I left the
included-p stuff out to implement it later, but I'm sure I wouldn't
have come up with something so clean. Viable indeed! It's a nice
lesson in coding style and reader macros.

Furthermore, as most of the time, improving the abstractions can have
great effect on the extensibility. Having a separate boundary class
makes it easy to implementing fuzzy intervals.

Thank you very much

A