Apple Macintosh OS X Software Development Programming
Macintosh Software Development
Version 1.7 Released
What is it?
ObjectiveCLIPS allows the creation of intelligent Cocoa applications with persistent object models and complex business rules. Out of the box, Apple gives you the ability to write Cocoa applications with dumb passive data models using CoreData. However, there is no convenient way to express complex constraints and dependent values without writing custom business objects. Even if you write the custom objects, your code will likely be fragile for a variety of reasons. ObjectiveCLIPS allows you to write rules about your objects and execute actions when rules match.
How is this better than creating custom subclasses of NSManagedObject?
Objects Are Intentionally Myopic. In other words, when designing a class, we intentionally make it ignorant of other classes as much as possible to limit coupling. This loose-coupling goal conflicts with the desire to write complex behaviors that depend on specific patterns of values that can span many objects. Performing the graph navigation and evaluation of these patterns by hand produces fragile code with excessive coupling. Making use of the powerful pattern matching of CLIPS, we can express these complex rules with a simple syntax and still keep our business objects generic.
How does it work?
I started with CLIPS. CLIPS is great as far as it goes and is really wonderfully extensible and modular, but it lacked a good scripting language and its function library wasn't very big. Additionally, it only provides a command line interface. This version of CLIPS is based on 6.23 with a small change to the parser to make the square bracket characters [ ] significant tokens.
I added the ability to map objects to deftemplate structured facts. Key Value Coding and Key Value Observing makes this synchronization fairly easy. Field names in the deftemplate are key paths into the object. ObjectiveCLIPS also adds a field 'self' to every CLIPS fact with an Objective C representation to make it easy to get the actual object. The mapping and synchronization is managed by a subclass of NSManagedObjectContext called KBManagedObjectContext. The KBManagedObjectContext also hangs onto the CLIPS runtime engine which is represented by the KBEnvironment object.
After working with this for awhile I began to wish for
more power in sending messages to the Objective C objects and I
integrated FScript . FScript
integration is straightforward in CLIPS, you simply provide blocks
using FScript/Smalltalk block syntax. The block must be enclosed in
Variables that are bound by CLIPS appear in the FScript block using CLIPS variable syntax. Locally declared FScript variables do not have the leading question mark. FScript can also be used in predicates on the left hand side of rules and FScript can be used to implement free functions using deffunction in CLIPS. (For the curious, this is accomplished in CLIPS by creating a user function with the name '[' and providing a custom parser routine for this function that reads up until the matching ']' while mapping variable bindings from CLIPS to FScript's interpreter)
CLIPS Expert System Shell
Notice that the above rule uses CLIPS fact variable binding to get a reference to the non-pink elephant. Fact variables and Objective C object references can be used interchangeably in FScript. For instance, I could have written the rule like this:
Using Objective C runtime introspection, CLIPS, and FScript, it is possible to write intelligent Cocoa applications. Any Objective C object can be asserted as a fact in CLIPS, however, CoreData provides an additional meta model along with object persistence.
Don't use the CLIPS 'modify' function in your rules!
Sync is designed to work one way in Objective CLIPS. You match facts, but you modify objects. Consequently, using the CLIPS assert, retract, or modify operations on facts backed by Objective C objects can produce memory leaks and unpredictable results. Always change fact values by sending the appropriate message to the object using FScript. Note that FScript treat's NSManagedObject subclasses specially and accepts both setFoo: as well as setValue:forKey:'foo' even if no setFoo: method has been defined.
Changes to CLIPS
As mentioned above, I added a couple lines to the scanner to treat square brackets as significant characters.
Which FScript Version?
You need FScript.framework version 1.3.1 or later from FScript.org
CLIPS, FScript, and ObjectiveCLIPS all have pretty much
the same BSD style license. I did it because it was cool, fun, and
useful. On the other hand, it was a lot of work so if it helps you out,
saves you lots of time, and you're feeling grateful at all, consider
making a donation towards future enhancements and maintenance. All
major credit cards are accepted.
I'm happy to answer quick questions by email and am available to do consulting and short training courses. Contact me to discuss how I can best meet your needs.
Version 1.7 has been extensively optimized for production use when the developer tools are not in use. This makes shipped applications much faster by eliminating unnecessary processing and data synchronization. In addition, extensive trace logging can be enabled from a checkbox on the CLIPS console window and many of the debugging capabilities are now lazily initialized to avoid paying for features that are not being used (such as synchronization of KBActivations).
Also, the FScript bridge has been rewritten to eliminated redundant parsing by caching FScript expressions as Blocks. I would like to thank the people at Marware for all their help and patience in this optimization effort.
What was new in version 1.2?
The agenda has been exposed via the method activations on KBEnvironment. This returns a list of KBActivations which contains a handle to the activated rule as well as the supporting facts. The user interface for viewing activations in the Tools menu is now implemented as well so you can watch the agenda change while developing.