Search code examples
clips

Can't reference inherited slot in a CLIPS message handler


I have an abstract class with one defined read-only slot named storage-size:

(defclass digital-media (is-a USER)
    (role abstract)

    (slot storage-size
        (type INTEGER)
        (default -1)
        (access read-only)
        (visibility public)))

I've attached a primary message handler to my abstract class which references the same storage-size slot.

(defmessage-handler digital-media read-storage primary (?offset ?size)
    (if (or (< ?offset 0) (>= ?offset ?self:storage-size))
        then
        (printout t "Read offset " ?offset " is out of bounds, max storage size is " ?self:storage-size crlf)
        (halt))

    (printout t "Still running for some reason..." crlf))

When making a concrete class, the inherited field works as expected only if it's implicitly declared:

(defclass compact-disk (is-a digital-media)
    (role concrete))

(make-instance my-disk of compact-disk)
(send [my-disk] print)
; [my-disk] of compact-disk
; (storage-size -1)

(send [my-disk] read-storage 128 1024)
; Read offset 128 is out of bounds, max storage size is -1

However, when I provide a default value for it the message handler doesn't work:

(defclass compact-disk (is-a digital-media)
    (role concrete)

    (slot storage-size
        (source composite)
        (default 650)))

(make-instance my-disk of compact-disk)
(send [my-disk] print)
; [my-disk] of compact-disk
; (storage-size 650)

(send [my-disk] read-storage 128 1024)
; [MSGPASS3] Static reference to slot storage-size of class digital-media does not apply to [my-disk] of compact-disk
; [ARGACCES5] Function >= expected argument #2 to be of type integer or float
; [PRCCODE4] Execution halted during the actions of message-handler read-storage primary in class digital-media
; FALSE

Solution

  • I don't fully understand the rationale, but when you change the slot facets for a subclass, the superclass message-handlers can't use slot shorthand references for that slot when called by an instance of the subclass. So in the read-storage handler you'll need to change ?self:storage-size to (send ?self get-storage-size).

    I suppose this enforces encapsulation of the superclass. For example, the read-storage message-handler knows the ?self:storage-size reference must be an integer and that it's not possible for a subclass to redefine the type of this slot to a string and then invoke this message-handler with a subclass instance containing a string rather than an integer value for the slot.