Sheeple is a Dynamic, CLOS-like, Delegative Prototype-based Object-Oriented Programming Framework (or "POOP Framework") that strives to optimize application flexibility, minimize cost while increasing value, maximize programmer resources, and empower application implementers to better assist them in leveraging modern paradigms in order to proactively achieve next-generation synergy in tomorrow's web 3.0 world. It is implemented in (mostly) ANSI Common Lisp. Sheeple is fully buzzword compliant.
Sheeple was originally written as a persistent library for use with Sykosomatic. Because of a desire to use it in other applications, it was rewritten as a regular prototype object system and provided as a standalone library meant for general-purpose OO hackery.
Persistent Sheeple has not been abandoned. Instead, it's being implemented as a separate library using Sheeple's reflection features.
Sheeple is inspired by a number of Object-Oriented systems, mainly:
CLOS, the Common Lisp Object System.
Slate, a Smalltalk-like language with Prototype Multiple Dispatch that Sheeple bases its message dispatch system on.
Self, another Smalltalk-like language with lots of papers written about it, and the concept of prototype OO. The Self compiler's maps were also an inspiration for Sheeple's molds.
Io, a pure prototype-based language. Sheeple, like Io, implements differential inheritance as a central thing.
It was written with the purpose of providing a lot of the goodies of CLOS programming in a completely prototype-based environment. As such, it shares a lot of features and syntax with CLOS. The most notable of these are multiple inheritance and multiple dispatch of methods.
Confirmed to work (pass all tests):
- SBCL 1.0.33
- Clozure CL 1.4
- GNU CLISP 2.48
- Allegro CL 8.1
Sheeple doesn't use any OS-specific code (or threads), so it should be stable on all operating systems. Sheeple has been tested on OSX, Linux, and Windows Vista. In theory, it should work on any implementation that supports hash tables with key weakness. If you run the test suite on a platform not listed above, and all tests pass, please e-mail me and I'll add it to the list of supported platforms.
It's fairly effortless to get Sheeple working. To get started, simply
(asdf:oos 'asdf:load-op 'sheeple) (in-package sheeple-user)
And mess around from there. Be aware that if your implementation does not include ASDF, you will have to acquire it and load it yourself. CLISP, for example, will require this. For information on how to do this, check out Cliki, or the ASDF Homepage.
Sheeple should work on most Lisp implementations that support weak pointers and weak hash tables, although it's mainly developed and tested on SBCL and Clozure CL.
Sheeple also includes a suite of tests you can run on whatever platform you're trying to get it to work on. The test suite depends on Fiveam. To run it:
(asdf:oos 'asdf:test-op 'sheeple)
And watch the tests scroll by. All tests should pass as long as you're using a tagged release on a supported platform.
There are mailing lists set up in common-lisp.net. Please refer to http://common-lisp.net/project/sheeple for more information.
Dynamic object management tools (inspection of objects, addition/removal of properties, all without requiring full redefinition/object updating).
Differential inheritance of property values
Efficient property access.
Mostly efficient dispatch (WIP)
Integration with built-in Lisp types through autoboxing.
Multiple inheritance with dynamic inspection and management (adding/removing) of parents.
Multiple dispatch on replies (methods) -- replies specialize on specific objects, and follow their inheritance hierarchies. Reply definition is almost identical to the core of CLOS methods, and shares similar semantics.
CLOS-style standard combination for replies (:before, :after, and :around)
Sheeple includes a user manual with a full CLHS-style specification of the API. The manual can be built from doc/, using make. This requires texinfo to be installed. You can fetch a prebuilt manual from http://sykosomatic.org/sheeple/sheeple.pdf.
For a quicker introduction, you can also take a peek at the examples/ directory.
As usual, you can always contact me directly with questions. I also regularly lurk in #lisp@freenode (as zkat).
This creates a standard object with only =STANDARD-OBJECT= as its parent
SHEEPLE-USER> (defparameter *my-object* (object)) *MY-OBJECT*
We can add properties to an object at any time. This particular call also auto-generates an accessor based on the property name given.
SHEEPLE-USER> (setf (property-value *my-object* 'var :accessor t) "value") #<Object #x1579A04E> SHEEPLE-USER> (var *my-object*) "value"
Creating a new object is as simple as calling OBJECT.
SHEEPLE-USER> (defparameter *child* (object :parents (list *my-object*))) *CHILD*
Sheeple does differential inheritance by default. This means that value lookup fetches the first available value found in the object's hierarchy list, unless a value is set directly in an object.
SHEEPLE-USER> (var *child*) "value" SHEEPLE-USER> (setf (var *my-object*) "new-value") "new-value"
We've changed my-object's VAR value, so child's VAR value will also change.
SHEEPLE-USER> (var *my-object*) "new-value" SHEEPLE-USER> (var *child*) "new-value"
Replies are to Messages as Methods are to Generic Functions, and the semantics are very similar (at least on the surface). Note that Sheeple handles built-in Lisp objects, and can dispatch on them. They are named just like the lisp types they refer to, with the =foo= naming scheme for prototypes.
SHEEPLE-USER> (defmessage synergize (a b)) #<Message: SYNERGIZE #x157AAC36> SHEEPLE-USER> (defreply synergize ((a =string=)) (b =string=))) (concatenate 'string a b)) #<Reply: SYNERGIZE #x157D90FE> SHEEPLE-USER> (defreply synergize ((a =number=) (b =number=)) (+ a b)) #<Reply: SYNERGIZE #x15815B76> SHEEPLE-USER> (synergize "foo " "bar") "foo bar" SHEEPLE-USER> (synergize 5 5) 10
Replies are defined on sheep objects themselves. No need for eql-specialization, everything is an 'instance' in Sheeple.
SHEEPLE-USER> (defreply synergize ((a *my-object*) (b *my-object*)) (object :parents (list a b))) #<Reply: SYNERGIZE #x157C5596> SHEEPLE-USER> (synergize *my-object* *child*) ; ERROR - circular precedence graph ; Evaluation aborted. SHEEPLE-USER> (synergize *child* *my-object*) #<Object #x15787456> SHEEPLE-USER> (var (synergize *child* *my-object*)) "new-value"
Finally, since objects are fully-dynamic, you can even add and remove parents by simply setting its object-parents place:
SHEEPLE-USER> (defparameter *my-object* (object)) *MY-OBJECT* SHEEPLE-USER> (object-parents *my-object*) (#<Object =STANDARD-OBJECT= #x3000413F556D>) SHEEPLE-USER> (push (object :nickname 'some-other-object) (object-parents *my-object*)) (#<Object SOME-OTHER-OBJECT #x30004159311D> #<Object =STANDARD-OBJECT= #x3000413F556D>) SHEEPLE-USER> (object-parents *my-object*) (#<Object SOME-OTHER-OBJECT #x30004159311D> #<Object =STANDARD-OBJECT= #x3000413F556D>)