PicoLisp Explored: Object-Oriented Programming, Part 2
Mia
Posted on February 1, 2022
In the previous post, we showed how to define classes and their methods and properties in PicoLisp using the class
and dm
functions.
We have already created the classes +Shape
and its two subclasses +Rectangle
and +Circle
with a few methods. You can find the code here or in the previous post. As you might remember, per syntax convention class names start with a +
and methods end with a >
.
Now we will show how we can create objects from that classes and use them. Let's open our file with the class definitions and run it with: $ pil shape.l +
(the +
opens the debugger).
Let's create a rectangle object!
We can create a new object using the function new
. A rectangle object also has four properties, X Y DX DY
that also need to be defined in the function call. For example, the code below defines a rectangle with width=30 and height=20, with the reference point (x,y) in (0,0).
(new '(+Rectangle) 0 0 30 20)
Let's set it to the variable R
.
: (setq R (new '(+Rectangle) 0 0 30 20))
-> $134432824
The return value is a so-called "anonymous symbol". However, it's not so relevant for the moment, so we will not dig further into that now.
We can check R
using show
:
: (show R)
$134432824 (+Rectangle) # Show the rectangle
dy 20
dx 30
y 0
x 0
Use the methods
Now let's use the area>
and perimeter>
functions that we defined before. The syntax is pretty straightforward:
: (area> R) # Calculate area
-> 600
: (perimeter> R) # and perimeter
-> 100
Now let's move the origin:
: (move> R 10 5) # Move 10 right and 5 down
-> 5
: (show R)
$134432824 (+Rectangle)
y 5 # Origin changed (0,0) -> (10,5)
x 10
dy 20
dx 30
Note that although the method move>
was not defined for the +Rectangle
class, it's still available since it is inherited from the +Shape
superclass.
We can do the same also with the circle class. Since it is really identical, I will not write it here, but you can check it [in the source code of the tutorial]((https://gitlab.com/picolisp-blog/web-applications/-/blob/main/docs/shape.l).
Applying list functions
The objects can be used like any other symbols. For example they can be grouped to lists and we can apply list functions on them.
As an example, let's group the rectangle R
and the circle C
to a list and create a new list with their respective areas by applying mapcar 'area>
on them:
: (mapcar 'area> (list R C))
-> (600 2827)
Or we could move all list items by 10 items down and right. Let's do this with an anonymous function:
: (mapcar 'area> (list R C)) # Get list of areas
-> (600 2827)
: (mapc
'((Shape) (move> Shape 10 10)) # Move all 10 right and down
(list R C) )
-> 25
mapcar
and mapc
both apply a defined function to each element of a list. The difference is that mapcar
returns the whole list while mapc
rertuns only the result of the last evaluation. Here you can find an overview over the most important string functions.
Prefix Classes
Assume that we want to extend our shape system. From time to time, we need shapes that behave exactly like the ones above, but are tied to a fixed position. That is, they do not change their position even if they receive a move>
message.
One solution would be to modify the move>
method in the +Shape
class to a no-operation. But this would require to duplicate the whole shape hierarchy (e.g. by defining +FixedShape
, +FixedRectangle
and so on).
The PicoLisp Way is the use of Prefix Classes through multiple inheritance. It uses the fact that searching for method definitions is a depth-first, left-to-right search of the class tree. We define a prefix class:
: (class +Fixed)
(dm move> (DX DY)) # A do-nothing method
Now let's define a Fixed Rectangle by adding the prefix class when we create the object:
: (setq R (new '(+Fixed +Rectangle) 0 0 30 20)) # '+Fixed' prefix class
-> $134432881
Now the rectangle will not move even if we apply the move>
function:
: (move> R 10 5) # Send 'move>' message
-> NIL
: (show R)
$134432881 (+Fixed +Rectangle)
dy 20
dx 30
y 0 # Did not move!
x 0
Alternatively, it's also possible to define a new subclass +FixRect
that inherits from both +Fixed
and +Rectangle
:
: (class +FixRect +Fixed +Rectangle)
-> +FixRect
and then use it directly:
: (setq R (new '(+FixRect) 0 0 30 20))
-> $13455710
Internal Representation
In the PicoLisp Explored-series we always try to cover the internal representation of the data as well.
As you might know by now, PicoLisp has only three data types: numbers, symbols and lists. Where do we find objects and classes in this model? Do we need to change it?
Of course not - objects as well as classes are both implemented as symbols. In fact, there is no formal difference between objects and classes; classes are more a conceptual design consideration in the head of the programmer than a physical reality.
We will find many examples for class concepts in the next posts of the Web Application Tutorial.
Sources
Posted on February 1, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.