TheSwamp

Code Red => AutoLISP (Vanilla / Visual) => Topic started by: JohnK on February 28, 2023, 09:55:55 AM

Title: Bottom-up design
Post by: JohnK on February 28, 2023, 09:55:55 AM
Bottom-up programming is essentially about simplifying the interface you program in (-i.e. Lisp can be altered by defining functions which can alter the programming methods you use).

For example: Instead of defining/designing functions like this:

Code - Auto/Visual Lisp: [Select]
  1. ; Main function: behave
  2. (defun behave (animal)
  3.   (cond
  4.     ((eq animal 'dog)
  5.      (dog
  6.        (mapcar 'eval '(wag-tail bark))))
  7.     ((eq animal 'pig)
  8.      (pig (mapcar 'eval '(rut oink))))
  9.     ;...

which are difficult to extend, debug, read, and use we can make our code easier to use by adopting the bottom-up programming mythology.

I will demonstrate how to do bottom-up design, but first we need a few simple functions which you can use throughout this tutorial.

Code - Auto/Visual Lisp: [Select]
  1. ; Simple functions common to all below.
  2. (defun wag-tail () (princ "wag, wag."))
  3. (defun bark () (princ "woof, woof."))
  4. (defun rut () (princ "roll in mud."))
  5. (defun oink () (princ "oink, oink."))
  6. (defun rub-legs () (princ "trip owner."))
  7. (defun scratch-carpet () (princ "destroy owner's stuff."))

We are used to setting up a function(s) that looks like this:

Code - Auto/Visual Lisp: [Select]
  1. ; Main function: behave
  2. (defun behave (animal)
  3.   (cond
  4.     ((eq animal 'dog)
  5.      (dog
  6.        (mapcar 'eval '(wag-tail bark)))
  7.          )
  8.     ((eq animal 'pig)
  9.      (pig (mapcar 'eval '(rut oink)))
  10.          )
  11.     ((eq animal 'cat)
  12.      (cat (mapcar 'eval '(rub-legs scratch-carpet)))
  13.          )
  14.     )
  15.   (princ)
  16.   )
  17.  
  18. ; Support function: dog
  19. (defun dog (behavior)
  20.   (mapcar
  21.     (function
  22.       (lambda (behavior)
  23.         (eval (list behavior))))
  24.     behavior)
  25.   )
  26.  
  27. ; Support function: pig
  28. (defun pig (behavior)
  29.   (mapcar
  30.     (function
  31.       (lambda (behavior)
  32.         (eval (list behavior))))
  33.     behavior)
  34.   )
  35.  
  36. ; Support function: cat
  37. (defun cat (behavior)
  38.   (mapcar
  39.     (function
  40.       (lambda (behavior)
  41.         (eval (list behavior))))
  42.     behavior)
  43.   )

Usage:

Code - Auto/Visual Lisp: [Select]
  1. (behave 'dog)
  2. (behave 'pig)
  3. (behave 'cat)

As you can see we start off designing the `behave` function to do something we want based on certain conditions. We can call `behave` to something different based on the condition we give (-i.e. behave like a Dog/Pig/Cat/etc.). This code works but is not very flexible if we need to add a condition like "owl", "horse" or "mouse" or etc.. To add functionality, we'd need to build support functions and modify the main function `behave`. The amount of maintenace needed to extend the tool is entirely wasteful and can be avoided if we utilize bottom-up programming.

For example, It can be more flexible if we built the `behave` function more generically and supplied the methods we want to preform. Like the revision below (NOTE: below I am using SET instead of DEFUN, but there is no difference in operation between SET and DEFUN when used like this, only used to highlight and introduce). This simple redesign of the `behave` function will allow us to extend the conditions and cut down on maintenace time and mistakes.

Code - Auto/Visual Lisp: [Select]
  1. (defun behave (animal behavior)
  2.   (eval (list animal (quote behavior)))
  3.   (princ)
  4.   )
  5.  
  6. (set 'dog
  7.      (lambda (behavior)
  8.        (mapcar
  9.          (function
  10.            (lambda (behavior)
  11.              (eval (list behavior))))
  12.          behavior)))
  13.  
  14. (set 'pig
  15.      (lambda (behavior)
  16.        (mapcar
  17.          (function
  18.            (lambda (behavior)
  19.              (eval (list behavior))))
  20.          behavior)))
  21.  
  22. (set 'cat
  23.      (lambda (behavior)
  24.        (mapcar
  25.          (function
  26.            (lambda (behavior)
  27.              (eval (list behavior))))
  28.          behavior)))

Usage:

Code - Auto/Visual Lisp: [Select]
  1. ; Now you can call `behave` like this:
  2. (behave 'dog '(wag-tail bark))
  3. (behave 'pig '(rut oink))
  4. (behave 'cat '(rub-legs scratch-carpet))

This simple redesign allows us to create as many `animals` and `behaviors` as we'd like and this redesign is a lot like using "methods" in "Object Orientated programming" (OOP) which is what bottom-up programming is about (we can change how we USE lisp). By using this method we essentially define--on the fly--what methods are sent to the `behave` function instead of using the predefined list.

A word about why I used SET instead of DEFUN in the last example. This is a on-going developing quirk of personal preference. In this case, I think of `dog`, `pig`, and `cat` as more of a `symbol` with a `method` then a full fledged `function` with an `operation`. I believe the use of DEFUN may be faster but again this is more of a personal preference thing. However the SET functions have a type of USUBR instead of SUBR if that means anything to you.

Back to the bottom-up discussion; as you can see, we had to change the parameter list to the `behave` function and it's not quite as user friendly and as readable as the first example. Also, it still has the potential to support mistakes in coding so, we can rectify these short-comings by adding another function definition to build the functions based on the given arguments and thus make things a bit more user friendly and readable.

Code - Auto/Visual Lisp: [Select]
  1. ; Define a simpler version of `behave`
  2. (defun behave (animal)
  3.   (eval (list animal))
  4.   (princ)
  5.   )
  6.  
  7. ; Build a function generator helper.
  8. (defun define (binding body)
  9.   (set
  10.     binding
  11.     (eval
  12.       (append
  13.         (list 'lambda nil)
  14.         (mapcar 'list body)
  15.         )
  16.       )
  17.     )
  18.   )

Usage:

Code - Auto/Visual Lisp: [Select]
  1. ; Define what the dog function should be.
  2. (define 'dog '(wag-tail bark))
  3. (define 'pig '(rut bark))
  4. (define 'cat '(rub-legs scratch-carpet))
  5.  
  6. ; call the behave function.
  7. (behave 'dog)
  8. (behave 'pig)
  9. (behave 'cat)

With a little bit of setup--one helper function--we can change the way we use the language (-i.e. to be more clear) with very little overhead.

This also has the added benefit of making things easier to debug (can you spot my mistake; I'll give you a hint: silly pig).

NOTE: according to my benchmarks this method also gives you a SLIGHT increase in speed of operation but that shouldn't be the major take away from the use of our `method like` design.
Title: Re: Bottom-up design
Post by: pkohut on February 28, 2023, 11:51:45 AM

A word about why I used SET instead of DEFUN in the last example. This is a on-going developing quirk of personal preference. In this case, I think of `dog`, `pig`, and `cat` as more of a `symbol` with a `method` then a full fledged `function` with an `operation`. I believe the use of DEFUN may be faster but again this is more of a personal preference thing. However the SET functions have a type of USUBR instead of SUBR if that means anything to you.

Pretty nice overall example and thanks for clarifying the usage of SET.  :smitten:
Title: Re: Bottom-up design
Post by: kdub_nz on February 28, 2023, 02:19:47 PM

Nice discussion point John.

without meaning to distract from the theme ;
In your set statements I mentally translated 'behaviour' to 'action' . . . but a rose by any name is still a rose
Title: Re: Bottom-up design
Post by: VovKa on February 28, 2023, 04:46:29 PM
i vote for associations lists
Title: Re: Bottom-up design
Post by: It's Alive! on February 28, 2023, 05:22:43 PM
i vote for associations lists
like ((0 . dog)(1 . bark)(2 . eat))?
Title: Re: Bottom-up design
Post by: VovKa on February 28, 2023, 05:45:08 PM
like ((0 . dog)(1 . bark)(2 . eat))?
or
Code: [Select]
(setq animals (list (list 'dog
  (list 'properties 'fluffy 'friendly 'noisy)
  (list 'abilities 'wag-tail 'bark 'bite)
    )
    (list 'cat
  (list 'properties 'fluffy 'unfriendly 'silent)
  (list 'abilities 'lick-tail 'meow 'bite)
    )
      )
)
(setq abilities (list (list 'wag-tail '(princ "wag, wag"))
      (list 'lick-tail '(princ "lick, lick"))
      (list 'bark '(princ "woof, woof"))
      (list 'meow '(princ "meow, meow"))
      (list 'bite '(princ "yam, yam"))
)
)