Permalink
Fetching contributors…
Cannot retrieve contributors at this time
693 lines (537 sloc) 30.3 KB

Vocabulary

Sheep

singular used to refer to a single Sheeple object.

Sheeple

plural used to refer to more than one sheep. Also used to refer to the project.

Property

In a sheep, a property is a key/value pair, with a symbol as a key. Data is stored in properties.

Direct Property

A property that is present directly in a particular sheep

Indirect Property

A property that a sheep has access to that is stored elsewhere in the hierarchy-list

T

the root object, ancestor of all sheep objects.

STANDARD-SHEEP

Ancestor of all actual sheep (the ones that aren’t built-ins).

Hierarchy List

An ordered set containing the order of delegation for a particular sheep. The set is fetched using SHEEP-HIERARCHY-LIST

Ancestor

Any sheep that appears in the SHEEP-HIERARCHY-LIST for any given sheep, with sheep removed. This list only includes direct ancestors, not objects that could be considered ‘cousins’.

Descendant

For a sheep X, any sheep Y whose hierarchy list will contain X, except Y itself.

Parent

A direct parent of a sheep, e.g. a sheep object present directly in a sheep’s sheep-parents list.

Child

An object that directly delegates to another sheep.

Sibling

Two sheeple that share the same hierarchy list are siblings

Message

A Generic function. Holds pointers to all existing replies (methods).

Reply

A message defines the prototype for how it is supposed to look, a reply defines the actual code to run, and when to run it.

Built-in

A built-in lisp type. Most lisp types have boxed-object prototypes assigned to them, so they can be fetched using their assigned names (such as string, list, etc) These prototypes can have replies defined on them.

Boxed object

An object (probably) autoboxed by Sheeple when appropriate (cloning and reply definition). These objects wrap any non-sheep Lisp object and allows definition of replies, addition of properties, etc (they’re just sheeple).

boxed-object

All boxed objects are descendants of the prototype ‘boxed-object, which is a delegate of T (this is important – messages defined on dolly won’t affect built-ins!!)

Delegation

Delegation is the act of not relying on an object’s locally-stored characteristics (such as roles and properties), and instead searching the sheep-hierarchy-list for appropriate data. Delegation of properties, for example, involves searching the sheep-hierarchy-list for a bound local property in one of the sheep’s ancestors, even though the property might not be directly bound in the sheep itself.

Cloning

Cloning involves a shallow copy of a sheep object that involves copying direct-properties, parents, and roles into the new object.

API Documentation

Sheep spawning

Sheeple does not have a concept of “classes”. Instead, new objects are created based on other objects. For the purposes of maintaining what would usually be a class hierarchy, one can treat sheeple as “prototypes” for other sheeple – it’s common to simply define an object for the sole purpose of cloning it. Sheeple can clone multiple objects at the same time, and the rules for dividing up behavior are the same CLOS uses (you can read the rules here: http://www.lispworks.com/documentation/HyperSpec/Body/04_ce.htm). The list of parents/delegates is dynamic. Parents can be added or removed at run time. New sheeple are created by using the the SPAWN function, and the DEFSHEEP and DEFPROTO macros.

Note to users familiar with other Prototype-OO systems: Sheeple differs from systems such as Slate and Self in that the default, preferred method of creating objects is not creating shallow copies (clones), but instead to create a “fresh” object, and delegate everything to a certain set of `parents’.

SPAWN function

Syntax: spawn &rest sheeple => new sheep

Args and values: sheeple – a list of lisp objects new sheep – a new sheep object with sheeple is its parents

Description: SPAWN creates a new STANDARD-SHEEP instance with SHEEPLE as its parents. Any non-sheep objects in SHEEPLE will be autoboxed and -those- objects will be added as parents. The new sheep’s parents list will be ordered the same as they are given. An empty parent list when using SPAWN will automatically set STANDARD-SHEEP as the new object’s sole parent.

Examples:

(spawn) > #<Sheep #x15007E06> (sheep-parents (spawn)) => (#<Sheep =STANDARD-SHEEP #x14C8947E>) (spawn (spawn)) => #<Sheep [=STANDARD-SHEEP=] #x15007E06>

Side effects: Parents are added using ADD-PARENT, which may have side-effects.

DEFSHEEP macro

Syntax: defsheep ({parent-object}*) ({property-spec}*) option-spec* => new sheep

parent-object ::= a lisp object to be cloned. property-spec ::= (property-name property-value property-option*) property-name ::= symbol property-value ::= object property-option ::= {:reader {reader-message-name | nil}}* | {:writer {writer-message-name | nil}}* | {:accessor {reader-message-name | t | nil}}* message-name ::= {symbol | (quote (setf symbol))} property-option ::= (:nickname lisp-object) | (:documentation docstring)

Args and values: property-name – a non-keyword symbol, unquoted. property-value – any lisp object, used as the local value for this property reader/writer/accessor – can be supplied more than once for each property. if NIL is given for any of these, signals an error if there is another matching definition. :accessor creates both a reader and a writer with format (accessor-name sheep-object) (setf (accessor-name sheep-object) new-value). Providing T as the argument to :accessor automatically creates an accessor for that property using the given property name.

nickname – Set as the nickname for the new sheep during initialization documentation – set as the docstring for the new sheep during initialization

Description: DEFSHEEP creates a new sheep object that delegates to PARENT-OBJECT*. After the new object has been initialized, direct-properties and readers/writers/accessors are added to the new object based on each property-spec.

Examples:

(defsheep () ((var “value”))) => #<Sheep [=STANDARD-SHEEP=] #x1503119E> (defsheep (*) ()) => #<Sheep [=STANDARD-SHEEP=] #x14FB626E> (sheep-parents ) => (#<Sheep [=STANDARD-SHEEP=] #x1503119E>) (available-properties *) => (VAR)

Side-effects: Add-property and add-parent are both called repeatedly for each property-spec and parent-object. Add-property destructively adds the new properties to the new sheep.

DEFPROTO macro

Syntax: defproto proto-name ({parent-object}*) ({property-spec}*) option-spec* => new sheep

proto-name ::= symbol parent-object ::= a lisp object to be cloned. property-spec ::= (property-name property-value property-option*) property-name ::= symbol property-value ::= object property-option ::= {:reader {reader-message-name | nil}}* | {:writer {writer-message-name | nil}}* | {:accessor {reader-message-name | t | nil}}* message-name ::= {symbol | (quote (setf symbol))} property-option ::= (:nickname lisp-object) | (:documentation docstring)

Args and values: proto-name – a non-keyword symbol property-name – a non-keyword symbol property-value – any lisp object, used as the local value for this property reader/writer/accessor – can be supplied more than once for each property. if NIL is given for any of these, signals an error if there is another matching definition. :accessor creates both a reader and a writer with format (accessor-name sheep-object) (setf (accessor-name sheep-object) new-value). Providing T as the argument to :accessor automatically creates an accessor for that property using the given property name.

nickname – Set as the nickname for the new sheep during initialization documentation – set as the docstring for the new sheep during initialization

Description: DEFPROTO defines a new proto sheep based on PARENT-OBJECT*. The sheep object is bound to a special variable called PROTO-NAME. After the new object has been initialized, direct-properties and readers/writers/accessors are added to the new object based on each property-spec. Unlike DEFSHEEP, DEFPROTO automatically defines accessors for each property-spec using its property-name, unless :reader, :writer, and/or :accessor are given (in which case only the provided option is used). Unless explicitly given a :nickname option, DEFPROTO uses PROTO-NAME as the new sheep’s nickname. If a DEFPROTO form with a PROTO-NAME that points to an existing prototype is evaluated, REINIT-SHEEP is first called on the existing proto sheep, using parent-object* as its :new-parents argument. REINIT-SHEEP clears out all existing properties. After REINIT-SHEEP is called, new properties are added to the proto sheep according to the property-specs given in the new DEFPROTO form.

Examples:

(defproto test-proto () ()) > #<Sheep =TEST-PROTO #x150712B6>

Notes: While there is nothing to programmatically enforce it, prototypes declared with DEFPROTO use the foo naming convention to tag the prototype as such.

Sheep manipulation

Sheep Inspection

PARENTP

Syntax: parentp maybe-parent child => generalized-boolean

Arguments and values: maybe-parent – a sheep that may or may not be a parent child – a sheep whose parent list is checked

Description: Returns true if MAYBE-PARENT is a parent of CHILD; otherwise, returns false

Examples:

(let* ((parent (spawn)) (child (spawn parent))) (parentp parent child) => Generalized truth value (parentp child parent)) => NIL

ANCESTORP

Syntax: ancestorp maybe-ancestor descendant => generalized-boolean

Arguments and values: maybe-ancestor – a sheep that may or may not be a ancestor descendant – a sheep whose ancestor list is checked

Description: Returns true if MAYBE-ANCESTOR is a ancestor of DESCENDANT; otherwise, returns false

Examples:

(let* ((ancestor (spawn)) (descendant1 (spawn ancestor)) (descendant2 (spawn descendant1))) (ancestorp ancestor descendant1) => generalized truth value (ancestorp descendant1 ancestor) => NIL (ancestorp ancestor descendant2)) => generalized truth value

CHILDP

Syntax: childp maybe-child parent => generalized-boolean

Arguments and values: maybe-child – a sheep that may or may not be a child parent – a sheep whose child list is checked

Description: Returns true if MAYBE-CHILD is a child of PARENT; otherwise, returns false

Examples:

(let* ((parent (spawn)) (child (spawn parent)) (descendant (spawn child))) (childp child parent) => generalized truth (childp parent child)) => NIL (chldp descendant parent) => NIL

DESCENDANTP

Syntax: descendantp maybe-descendant ancestor => generalized-boolean

Arguments and values: maybe-descendant – a sheep that may or may not be a descendant ancestor – a sheep whose descendant list is checked

Description: Returns true if MAYBE-DESCENDANT is a descendant of ANCESTOR; otherwise, returns false

Examples:

(let* ((ancestor (clone)) (descendant1 (clone ancestor)) (descendant2 (clone descendant1))) (descendantp descendant1 ancestor) => generalized truth (descendantp ancestor descendant1) => NIL (descendantp descendant2 ancestor)) => generalized truth

ADD-PARENT

Syntax: add-parent new-parent sheep => modified sheep

Arguments and values: new-parent – a lisp object to be added as a parent to sheep sheep – the sheep who gets a new mommy

Description: The function ADD-PARENT receives a NEW-PARENT sheep, and a SHEEP. It pushes NEW-PARENT into the beginning SHEEP’s parents list if NEW-PARENT is not already there. If using ADD-PARENT would result in a cyclic hierarchy list, a SHEEP-HIERARCHY-ERROR condition is signaled, and the SHEEP is unaffected. The new-parent is added to the front of the direct-parents list, like a stack push. Returns the SHEEP object.

(add-parent mommy kiddo) => modified kiddo with mommy as parent. (add-parent kiddo’s-descendant kiddo) => ERROR: SHEEP-HIERARHY-ERROR

Side effects: SHEEP’s parents list is replaced with a list where NEW-PARENT is in the first position.

REMOVE-PARENT

Syntax: remove-parent parent sheep => modified sheep

Arguments and values: parent – a lisp object to remove from SHEEP’s parents list. If it is not a sheep object, it is autoboxed before the comparison is made. sheep – The sheep being abandoned by its bum drunk father

Description: If PARENT exists in SHEEP’s parents list, it is removed. Otherwise, an error is signaled.

(remove-parent daddy kiddo) => KIDDO object, without daddy as its parent. (remove-parent daddy kiddo) => ERROR

Side effects: PARENT is no longer part of SHEEP’s life. Alcoholism and chronic depression are common.

Properties

The most important part about properties in Sheeple is that a sheep delegates not just the existence of a property, but the current value as well. Sheeple decides which property value to use by following a sheep’s hierarchy list for the nearest available value. Thus, setting a property directly always overrides any delegation.

WITH-PROPERTIES/WITH-ACCESSORS

The WITH-PROPERTIES macro is identical in function to the with-slots macro that CLOS uses. Also, the default CLOS WITH-ACCESSORS can be used normally with sheeple. Another macro called WITH-MANIPULATORS, identical to WITH-ACCESSORS is available, but it is deprecated.

ADD-PROPERTY

Adds a direct property to SHEEP. Optional Readers and Writers must be a list of valid function names (in symbol or cons form) that will be used to create responses specialized on SHEEP. If make-accessors-p is T, the symbol in PROPERTY-NAME will be used to generate accessors with the format Reader=PROPERTY-NAME, Writer=(SETF PROPERTY-NAME). Returns the modified sheep.

(add-property sample-sheep ‘prop-name “value”) => modified sample-sheep

PROPERTY-VALUE

The function PROPERTY-VALUE receives two arguments: a sheep object and a property-name. It returns the value of that property, and searches indirect properties. There is no user-level function for only getting the direct property value of a sheep. Signals a condition of type UNBOUND-PROPERTY if there is no binding, direct or indirect, for that property-name.

(property-value sample-sheep ‘foo) => property value

DIRECT-PROPERTY-VALUE

This function works like PROPERTY-VALUE, but signals an UNBOUND-PROPERTY error if the sheep object does not hold the value locally.

(direct-property-value sample-sheep ‘foo) => direct value or ERROR

(SETF PROPERTY-VALUE)

The function (SETF PROPERTY-VALUE) sets a direct property on a sheep. If the property did not exist in the sheep’s hierarchy list, UNBOUND-PROPERTY is signaled. Returns the new value.

(setf (property-value sample-sheep ‘foo) ‘bar)

DIRECT-PROPERTY-P

The function DIRECT-PROPERTY-P returns T if a direct property was set on sheep, and NIL otherwise.

(direct-property-p sample-sheep ‘foo) => T

AVAILABLE-PROPERTY-P

The function AVAILABLE-PROPERTY-P returns T if the property is available to sheep, whether it’s direct or indirect.

(available-property-p sample-sheep ‘foo) => T

REMOVE-PROPERTY

The function REMOVE-PROPERTY receives a sheep and a property-name, and removes a direct property from SHEEP. It returns NIL if there was no direct property with that property-name set, and T if it removed an existing property.

(remove-property sample-sheep ‘foo) => T

PROPERTY-OWNER

The function PROPERTY-OWNER receives a SHEEP and a PROPERTY-NAME, and returns the sheep that sets a particular property visible to SHEEP, whether it be a direct-property or an indirect-property. If the property is unbound, it returns NIL by default. If a third argument is provided as T, UNBOUND-PROPERTY is signaled.

(property-owner sample-sheep ‘foo) => #<sheep that sets a direct-value for ‘foo>

AVAILABLE-PROPERTIES

The function AVAILABLE-PROPERTIES receives a SHEEP, and returns a list of available bound property specs for SHEEP. An empty list is returned if SHEEP has no available bound properties.

(add-property sample-sheep ‘baz ‘quux) (available-properties sample-sheep) ==> (<property spec: BAZ>)

Messages/Replies

Messages and Replies are very similar to CLOS’ generic functions/methods. Anyone familiar with CLOS programming should have a fairly easy time wrapping their head around Sheeple’s system.

DEFMESSAGE

The DEFMESSAGE macro is used to define a message that will hold different replies. It accepts an optional documentation option. Messages are automatically defined by defreply, but the preferred and recommended style is to define messages first. Not doing so signals STYLE-WARNING. Note: message and reply lambda-lists follow the rules described in http://www.lispworks.com/documentation/HyperSpec/Body/07_fd.htm

(defmessage synergize (foo bar) (:documentation “Synergizes FOO and BAR, preparing them for the Next Generation”))

DEFREPLY

The DEFREPLY macro defines a reply based on its arguments. Sheeple replies are left-weighted multireplies (by default) which can dispatch on multiple different arguments. The syntax follows closely with defmethod’s specialized lambda-list syntax, except the specializers are actual objects instead of class names. An unspecialized item in the lambda list will default to dispatching on DOLLY for that lambda-list property. The basic format is:

(defreply reply-name (plain-variable* (specialized-variable object)*) @body)

Specialized replies

Replies are specialized by replacing the variable with (specialized-variable object).

(defreply reply-name (foo (bar my-sheep)) (print “This is a specialized reply”))

Replies with unspecialized lambda lists are considered unspecialized, since they dispatch on the built-in sheep T, which is at the top of the hierarchy list for all sheep and builtins.

Reply combination

Reply definitions can also accept qualifiers. The standard reply combination supports three qualifiers: :before, :after, and :around.

(defreply reply-name :qualifier (…) body)

Primary replies

Replies without qualifiers are considered “primary” replies. It is required that a primary reply be available for any reply combination to happen.

CALL-NEXT-REPLY

This function can be called within the body of any reply. It will execute the next available applicable reply for the current call. You may pass new arguments to C-N-M, but it will not change the dispatch of the reply. (the next reply will be executed as if it had dispatched on the original arguments).

NEXT-REPLY-P

Within the body of a reply, you may use NEXT-REPLY-P to check for the presence of a next reply.

:BEFORE

Any available :before replies are executed before the execution of the primary reply(s). When used inside the body of a :before reply, CALL-NEXT-REPLY calls the next applicable :before reply, not the next primary one.

:AFTER

Any available :after replies are executed after the execution of the primary reply(s). When used inside the body of an :after reply, CALL-NEXT-REPLY calls the next most specific :after reply, not the next primary reply.

:AROUND

:around replies “wrap” around the current primary reply. CALL-NEXT-REPLY must be called in the body of an :around reply. C-N-M calls the most applicable primary reply, not the next most specific :around reply.

Huge example

This example illustrates usage of sheeple and replies:

(defproto sheep1 () ()) (defproto sheep2 () ())

(defreply do-it (foo) ;defines this reply on T (print foo)) (do-it “hey!”) => hey! ;the reply facility autoboxes regular lisp objects

(defreply do-it ((something sheep1)) (format t “~a is sheep1. FYI” something)) ;; lambda-list variables do not have to be the same across replies, ;; I only do it here for convenience. (do-it sheep1) => #<Sheep AKA: SHEEP1 {BA28B89}> is the first sheep. FYI

(let ((x 5)) (defreply do-it ((foo number)) ;if dispatching on builtins, use builtin prototype. (* x foo))) ;It captures the lexical environment, too. (do-it 5) => 10

(defreply do-it ((foo 5)) ;any object can be dispatched on. Non-sheep are autoboxed. (declare (ignore foo)) ;declarations work (print “FIVE!! THIS IS VERY EXCITING!!1”)) (do-it 5) => “FIVE!! THIS IS VERY EXCITING!!1”

(defreply do-it ((foo float)) (floatp float))

(do-it 5.0) => T ; Sheeple has a CLOS-like hierarchy for (do-it 5) => “FIVE!! THIS IS VERY EXCITING!!1” ; autoboxed built-ins. (do-it 6) => 12

(defreply synergize ((foo sheep1) (bar sheep2)) (print “I got sheep1 first, then sheep2”))

(defreply synergize ((foo sheep2) (bar sheep1=)) (declare (ignore foo bar)) (print “I got sheep2 first, then the first sheep.”))

(defreply synergize ((foo number) (bar number)) (+ foo bar)) (synergize 3 5) => 8 (synergize 4 “hey hey hey!”) => ERROR: NO-MOST-SPECIFIC-REPLY

(defreply synergize ((foo string) (bar string)) (concatenate ‘string foo bar)) (synergize “Hey ” “Jude”) => “Hey Jude”

;; :before and :after (defparameter test-sheep (defsheep () ((var “value” :accessor var)))) (var test-sheep) => “value”

(defreply var :before ((sheep test-sheep)) (setf (property-value sheep ‘var) “new-value”)) (var test-sheep) => “new-value” (setf (var test-sheep) “old-value”) (property-value test-sheep ‘var) => “old-value” (var test-sheep) => “new-value”

(defreply var :after ((sheep test-sheep)) (setf (property-value sheep ‘var) “after-value”)) (var test-sheep) => “new-value” (property-value test-sheep ‘var) => “after-value”

Potential gotchas:

CLOS (semi-)compatibility

For the time being, CLOS objects and CLOS-defined types are boxed as a clone of boxed-object, not as autoboxed versions of the classes/objects.

Message definition

The behavior for clobbering regular functions or generic functions with messages and vice-versa is undefined.

Threads

Give it a shot, tell me how it goes! :)

Implementation performance notes

Property access

As of version 1.0, property access is O(n), where n is the number of sheep that must be traversed in order to find a property value. Worst case happens when the property is completely unbound for a given sheep hierarchy. No caching is done right now, as possible caching schemes that are both fast at reading, and fast at writing are being evaluated. Realistically, sheep hierarchies are rarely very deep, so this should not become an issue for most cases. Once caching is implemented, this will become O(1).

Reply dispatch

Reply dispatches are cached on first execution and changed when appropriate. This makes dispatch essentially O(1), even for multireplies. Even though multireply dispatch is very fast, singly-dispatched accessors have not been optimized enough. Thus, for any purposes that require optimal read-access, directly accessing the properties with PROPERTY-VALUE is recommended. Optimization of accessors is planned.

Comparison to CLOS performance

Sheeple is still very young. Thus, it’s no surprise that a good implementation of CLOS will outperform it when used on the same hardware, with the same CL implementation. Nevertheless, tests run on SBCL and Clozure CL show that the performance difference is on the order of 5-10x for property access, and about 10-15x for reply vs method dispatch (although sheeple sometimes performs even slower). Hopefully, as sheeple matures, it will become as fast as an implementation like PCL. Fortunately, because of the similarities of the APIs, many of PCL’s optimizations can be translated to Sheeple itself.

MOP Documentation

Sheeple exposes a (currently) small metaobject protocol for inspecting and changing behavior of sheep objects. The protocol uses CLOS as a backend, so users may extend Sheeple behavior as desired using an API familiar to those who have used the CLOS MOP before.

Metaobject classes

STANDARD-SHEEP

This class defines default behavior for all SHEEP objects. Certain CLOS methods can be specialized on it in order to change behavior, and certain methods can be defined to get access to meta-information about the sheep object.

Metaobject generic functions

Sheeple exposes the following generic functions for various purposes.

Inspection

These functions can be used to get information about the sheep object. While the information they provide can be used to rebuild new sheeple objects, the effects of trying to alter them directly or indirectly are undefined unless otherwise stated. (don’t do it)

SHEEP-NICKNAME

This function returns the nickname assigned to this sheep object. This place can be SETFd in order to change the sheep’s nickname.

(sheep-nickname sheep) => string-nickname (setf (sheep-nickname sheep) new-nickname) => new-nickname (sheep-nickname sheep) => new-nickname

SHEEP-DOCUMENTATION

Like SHEEP-NICKNAME, but returns the documentation string for the sheep object. This value can be SETFd to alter the sheep’s documentation string.

(sheep-documentation sheep) => “This sheep is documented! I write good code!”

SHEEP-HIERARCHY-LIST

Returns an ordered list representing the precedence order used by Sheeple when either a method is called, or a property-value is requested.

(sheep-hierarchy-list sheep) => (SHEEP’s hierarchy list)

SHEEP-PARENTS

Syntax: sheep-parents sheep => direct-parents-list

Arguments and values: sheep – any non-orphaned sheep direct-parents-list – a list of the direct parents of the sheep

Description: Returns an ordered list of the sheep’s direct parent objects, in the order that they were provided to clone/defsheep/defproto. In a sense, this is the inverse function of CLONE.

Examples:

(setf parent (clone) child1 (clone parent) child2 (clone parent standard-sheep))

parent => #<Sheep #x30004135F9BD>

(sheep-parents child1) => (#<Sheep #x30004135F9BD>)

(sheep-parents child2) => (#<Sheep #x30004135F9BD> #<Sheep AKA: DOLLY #x30004129148D>)

SHEEP-DIRECT-ROLES

Returns an unordered set of ROLE metaobjects that represent what replies SHEEP is a participant in.

(sheep-direct-roles sheep) => (sheep’s direct role metaobjects)

SHEEP-DIRECT-PROPERTIES

Returns an unordered set of PROPERTY-SPEC metaobjects that represent property definitions as provided to (or built by) CLONE/DEFSHEEP/DEFPROTO

(sheep-direct-properties sheep) => (list of property-spec objects)

PROPERTY-SPEC-NAME

Called on a PROPERTY-SPEC metaobject, this returns the symbol name for this property.

(property-spec-name sheep-property-spec-object) => SYMBOL

PROPERTY-SPEC-VALUE

Called on a PROPERTY-SPEC metaobject, this returns this property’s current direct value.

(property-spec-value sheep-property-spec-object) => VALUE

PROPERTY-SPEC-READERS

Called on a PROPERTY-SPEC metaobject, this returns a list of names used to define reader replies that read this property’s value, specialized on the sheep object.

(property-spec-readers sheep-property-spec-object) => (list-of-reply-names)

PROPERTY-SPEC-WRITERS

Called on a PROPERTY-SPEC metaobject, this returns a list of names used to define writer replies that write this property’s value, specialized on the sheep object.

(property-spec-writers sheep-property-spec-object) => (list-of-reply-names)

Special messages

Additionally, some message (not generic functions) can be specialized on sheep objects to alter behavior of sheep in various situations..

PRINT-SHEEP

You can define replies on PRINT-SHEEP in order to change a particular sheep’s printout.

(defreply print-sheep ((sheep my-special-sheep) stream) (format stream “~a is the most specialest sheep ever!” sheep))

INIT-SHEEP

This message is called by all sheep-creation functions (clone, defsheep, defproto) after parents have been added to the new object. In the case of DEFSHEEP and DEFPROTO, this is called before the properties specified in their property-specs are added. No primary replies should be defined on this message, and :around replies MUST call-next-reply.

(defreply init-sheep :after ((sheep my-super-specialest-sheep!) &key) (add-property sheep ‘some-property “woooo”))

REINIT-SHEEP

This is called whenever a DEFPROTO form is evaluated for an existing sheep. No primary replies may be defined on this message, and :around replies MUST call-next-reply.