Help with FOOP

Started by cormullion, August 24, 2008, 10:07:14 AM

Previous topic - Next topic

cormullion

I've finally got round to trying to understand the FOOP features of newLISP, and any guidance would be appreciated! (Where is FOOPerman?:)



I decided to try to do something where I understood the problem domain reasonably well but was useful enough to be able to see whether there are any practical benefits to using FOOP. So although it's probably overkill, I thought I'd try to learn FOOP using date/times.



Here are my first ideas:



; class for time instant/instances
(define (Time:Time (t (date-value)) (zone 0))
    (list Time t zone))
   
(define (Time:show t)
    (println (date (t 1) (t 2))))

(define (Time:days-between t1 t2)
; return difference in days between two times
    (div (abs (- (t1 1) (t2 1))) (* 24 60 60)))

(define (Time:get-hours t)
; return hour
    (int (date (t 1) (t 2) {%H})))

(define (Time:get-day t)
; return day of week
    (date (t 1) (t 2) {%A}))

(define (Time:leap-year? t)
  (let ((year (int (date (t 1) (t 2) {%Y}))))
  (and (= 0 (% year 4))
       (or (!= 0 (% year 100))  (= 0 (% year 400))))))

; class for Durations
(define (Duration:Duration (d 0))
   (list Duration d))
   
(define (Duration:period-to-string d)
; convert period in days to day hour min sec string
  (letn
    ((s (mul 24 60 60 (d 1)))
     (secs (mod s 60))
     (mins (mod (div s 60) 60))
     (hours (mod (div s 3600) 24))
     (days (mod (div s 86400) 86400)))
   (format "%02dd %02dh %02dm %02ds" days hours mins secs)))

(context MAIN)

(set 'my-birthday (Time (date-value 2008 5 26)))
(set 'christmas-day (Time (date-value 2008 12 26)))
(set 'time-now (Time))

(:show my-birthday)
;-> Mon May 26 01:00:00 2008
(:show christmas-day)
;-> Fri Dec 26 00:00:00 2008
(println "hours " (:get-hours time-now))
;-> hours 17
(println "day " (:get-day time-now))
;-> day Sunday
(println "shopping days " (:days-between time-now christmas-day))
;-> shopping days 123.314919
(println "leap year " (:leap-year? my-birthday))
;-> leap year true

(println (set 'shopping-day-string (: period-to-string (Duration (:days-between time-now christmas-day)))))
;-> 123d 06h 57m 12s


It looks reasonably OK for such a simple problem domain. But I want to write some functions called 'adjust-hours' or something which increases a time by a given number of, say, days. I can't see how to pass a time instance to a function and modify the instance, other than by doing this. The pass by reference idea seems to work only with contexts... ?



(define (Time:adjust-days t n)
  (list Time (+ (* 24 60 60 n) (t 1)) (t 2)))

(println (set 'christmas-day (:adjust-days christmas-day 3))) ; ?
(println (:show christmas-day))


Is this the general idea of FOOP? It has promise and I can see how it might be good but I'd like to know where the problems are too.

m i c h a e l

#1
Hi cormullion!



Still here, although I've been recovering from gall bladder surgery, so my participation has been minimal.


Quote from: "cormullion"So although it's probably overkill, I thought I'd try to learn FOOP using date/times.


Actually, dates and times are perfect for this sort of thing.




Quote from: "cormullion"It looks reasonably OK for such a simple problem domain. But I want to write some functions called 'adjust-hours' or something which increases a time by a given number of, say, days. I can't see how to pass a time instance to a function and modify the instance, other than by doing this. The pass by reference idea seems to work only with contexts... ?


This is where FOOP most breaks with OOP. The objects are not modified, but a new object is returned instead. The best way to think of it is like this: the number objects (2, 5, 11) are not modified in case of addition or multiplication, but instead, new number objects are returned. Although this is not intuitive, especially with numbers, you can agree that adding 2 to 5 does not modify the 5 object but instead returns a new object—the 7 object.



While making new objects (notice I avoid calling FOOP objects 'instances', as they're not, strictly speaking, instances of a class) every time something changes seems wasteful, it is actually quite fast in newLISP (objects are just lists, after all) and makes FOOP free of side-effects.




Quote from: "cormullion"Code:



(define (Time:adjust-days t n)

  (list Time (+ (* 24 60 60 n) (t 1)) (t 2)))



(println (set 'christmas-day (:adjust-days christmas-day 3))) ; ?

(println (:show christmas-day))





Is this the general idea of FOOP?


Yes, this is the difference in a nutshell. Any change requires us to set a variable to the new object. Haskell is the same way. It is a pure functional language and does not allow side-effects. Actually, in the case you give above, it might be better to set the result to a new variable adjusted-christmas-day ;-)




Quote from: "cormullion"It has promise and I can see how it might be good but I'd like to know where the problems are too.


This is where experimentation with FOOP comes in. Any problems will best reveal themselves through normal use, as you have done here.



m i c h a e l

cormullion

#2
thanks michael - and I appreciate the response from you while on your sick bed too...!



You say that objects are not instances  I"m not too up in my oo-jargon, so I'm  wondering why not.



I'm comfortable with the idea of returning new objects, although it would be useful  if there was some kind of naming convention so that the function 'obviously' needs its result assigning to a new symbol.

Jeff

#3
Corm - you cannot destructively modify a FOOP object even with a macro.  When a method is called, the actual method is not called.  The : function is called, which first evaluates the first argument, which is presumed to be a FOOP list.  Having done that, it tries to find the method in the context.  So:


(:foo (bar "baz" "bat"))

Is actually:


(: foo bar "baz" "bat")

...where foo is a symbol that, inside of bar, points to a function that accepts a foop list as its first argument.



Even in a macro, which should *not* evaluate its arguments, the first argument *must* be evaluated in order to find the function that is being called.  Ergo, no destructive functions.  No side effects.
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

m i c h a e l

#4
Quote from: "cormullion"You say that objects are not instances I"m not too up in my oo-jargon, so I'm wondering why not.


First, I'll quote from an old email Lutz sent in response to my referring to objects as instances:


Quote from: "Lutz"To be an instance of something implies a structural similarity

between object and class. This is the case in classic OOP and template

OOP but not in FOOP, where the class contains only the method object

but doesn't model the object data in any way, which are generated

instead by the class constructor.


In Smalltalk, for instance (no pun intended), classes are objects, too, and they act as little object factories. So to say an object is an instance of a certain class (in Smalltalk) is completely correct.



In FOOP, things are a bit different. Quoting Lutz again:


Quote from: "Lutz"Our FOOP objects still encapsulate both: data and methods (via a

pointer to the context class), but are structurally very different

from the class context (which only has the methods) and not really an

instance (in a copy/template sense) of it.


m i c h a e l

cormullion

#5
Thanks Jeff - it's starting to de-mistify, and it's very helpful when you summarize so confidently!



As for the 'instances' - I'm looking at it from a more English-like angle, hence the confusion when confronted with the parallel language spoken by OOPers through the ages...



I'm suppose I'm looking for a word that describes the fact that a number of similar entities have been created according to a formula. 'instances' conveys that plurality, that 'many of one'-ness. I understand the argument that the class isn't a template for the objects that it creates. And yet my objects are of this form



(list Time t zone)



and this is what appears in the constructor too. Ah well, I'm only writing a very short section about this so I'll just gloss over the terminological difficulties, as usual... :)



Mixins next :)

Jeff

#6
Take a look at my util.lsp module on Artful Code.  It includes a macro called "with-slots".  If you write your classes to use an association list for their members:


(define (Point:Point x y)
  (list (context)
        (list (list "x" x)
              (list "y" y))))


Then, you can easily access the slots:


(set 'point (Point 10 20))
(with-slots (x y) point
  (println "(" x ", " y ")"))


It makes it much simpler to think in terms of a FOOP object.
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

cormullion

#7
Thanks Jeff. It looks good. Although I'll stick with a dependency-free version for my present purposes, the eventual time module might use that.