Features of polymorphism such as virtual functions are not implemented in SPAD as they would be on a fully object oriented language..
A classic example of where such a feature is used in Object Oriented (OO) programming is when dealing with shapes. We want to be able to have different types of shape such as rectangle and circle, each of these types would be able to render itself in a way that is specific to the individual shape, but we also want to be able to store shapes together (say in a list) in such a way that it does not matter what type of shape it is.
Lets try to do this in SPAD. First lets define a general shape category:
)abbrev category SCENE SceneShapeCategory SceneShapeCategory(): Category == Type with render:(n:%) -> Void add render(n:%):Void == sayTeX$Lisp "shape not defined" |
Then lets define a more specific instance of this shape, in this case a Rectangle:
)abbrev domain SCENERE SceneRectangle SceneRectangle(): T==C where NNI==> NonNegativeInteger T== SceneShapeCategory with sceneRectangle:(w:NNI,h:NNI) -> % render:(n:%) -> Void C== add Rep := Record(width: NNI,height: NNI) sceneRectangle(w:NNI,h:NNI): % == [w,h] render(n:%):Void == sayTeX$Lisp concat(["rectangle(",string(n.width),","_ ,string(n.height),")"])$String |
So how do I create an array of shapes but get them to render according to their original type? Well first I tried this:
(1) -> rec: List SceneShapeCategory := [sceneRectangle(2,4)$SceneShapeCategory] The right-hand side of the $ operator must be a package or domain name, but SceneShapeCategory is a category. |
That does not seem to work for categories so then I resorted to using 'pretend'
(1) -> rec: List SceneShapeCategory := [sceneRectangle(2,4) pretend SceneShapeCategory] LISP output: ((2 . 4)) Type: List(SceneShapeCategory) (2) -> render(first rec) >> System error: The value ((|render| ((|Void|) $)) T (ELT $ 6)) is not of type FUNCTION. |
But that is not going to work either because 'pretend' only works for things that have the same representation.
Another thing to try is to create a SceneShapeObject, which is a domain rather than a category, to use as our general shape object for putting in lists:
)abbrev domain SCENERE SceneShapeObject SceneShapeObject(): T==C where NNI==> NonNegativeInteger T== SceneShapeCategory with render:(n:%) -> Void C== add Rep := Record(width: NNI,height: NNI) render(n:%):Void == sayTeX$Lisp "shape has no specific render" |
but I still cant get anything like a virtual function:
(3) -> rec: List SceneShapeObject := [sceneRectangle(2,4)$SceneShapeObject] The function sceneRectangle is not implemented in SceneShapeObject . |
but I still cant get anything like a virtual function. So again lets resort to using 'pretend'
(4) -> rec: List SceneShapeObject := [sceneRectangle(2,4) pretend SceneShapeObject] LISP output: ((2 . 4)) Type: List(SceneShapeObject) (5) -> render(first rec7) shape has no specific render Type: Void |
So now we can call render, but its the render associated with SceneShapeObject, we have lost all association with the original sceneRectangle.
Implementing Virtual Functions
Since there is no support for virtual functions built in to Axiom/FriCAS we will have to try implementing it ourselves. The idea is to add a type to the representation then, when a function like render is called, it can be redirected to the appropriate object. So I have modified Rep and the render function here:
)abbrev domain SCENERE SceneShapeObject SceneShapeObject(): T==C where NNI==> NonNegativeInteger T== SceneShapeCategory with render:(n:%) -> Void C== add Rep := Record(width: NNI,height: NNI,type : Type) render(n:%):Void == t := n.type render(n) pretend t |
The Rep is also changed in SceneRectangle and also the constructor is changed to set the type:
)abbrev domain SCENERE SceneRectangle SceneRectangle(): T==C where NNI==> NonNegativeInteger T== SceneShapeCategory with sceneRectangle:(w:NNI,h:NNI) -> % render:(n:%) -> Void C== add Rep := Record(width: NNI,height: NNI,type : Type) sceneRectangle(w:NNI,h:NNI): % == [w,h,SceneRectangle] render(n:%):Void == sayTeX$Lisp concat(["rectangle(",string(n.width),_ ",",string(n.height),")"])$String |
But this does not work because the 'SceneRectangle' in the constructor tries to create the object, rather than a type, so we get an infinite loop when we try to create this instance.