SPAD Polymorphism

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.


metadata block
see also:
Correspondence about this page

This site may have errors. Don't use for critical systems.

Copyright (c) 1998-2023 Martin John Baker - All rights reserved - privacy policy.