ehdonhon has asked for the
wisdom of the Perl Monks concerning the following question:

Hello,

I have a rather complex shopping cart system which needs
to be able to figure out a lot of things about the prices
of the items in a user's cart. I need the system to
updatable by somebody that knows less about the system than
me. I think that the best way to do this is if I could
provide a configuation system that encodes a series of
rules about the system in a document that is
seperate from the actual program. Here are
some examples of the kinds of things I want to encode:

Widgets cost $250

Gizmos cost $125

All customers that have had at least three orders from
us in the past get a 20% discount.

Buy 3 Widgets and we'll throw in a Gizmo for free.

Certain classes of customers get 50% off. (This is
applied before the 20% discount)

Buy at least 10 Gizmos, and we'll reduce the cost by
$30 per unit (This applies after the 50% discount if they get it,
but before the 20% discount if they get that).

If you buy the service agreement that goes along with
the Widgets during this month, we'll sell you the first
year of the agreement for free when you sign up for at
least a three year agreement.

This seems to me like it could become very confusing,
and very ad-hoc in a short period of time. I really
hate to re-invent the wheel on this sort of thing (especially
when it is a very large wheel). I was wondering
if there were any formats that people have already
come up with for defining rules in systems like these?
I looked through CPAN, but nothing really jumped out at
me.

It would be nice to have a real configuration language,
perhaps which uses opencyc which
(when done) should be able to do the hard part. But not needed
I think for your immediate problem. Incidentally I did not know
about JESS, but did know about the CLIPS
expert system package which it grew out of, and
CLIPS is public domain. I have not used CLIPS but it does
have a rule-based mode. There are example CLIPS programs
here and
while it would be very cool to use CLIPS in Perl, I think this
sounds like overkill for your project.

I once had a problem where I thought of using rules and
ended up encapsulating page layout subroutines so that I
could write custom layout rules in a line or two of perl each.
The motivation was that we had no database or perl, but we
had a lot of content with similar layout for each hotel, plus
not enough time to do it all by hand, and knowledge that changes
might even so come in late (they did). This
let me build a thousand page static website for fourteen
hotels in the Mandarin Oriental group. Along with image compositing
code I built with the gimp's perl server it resized all the photos
and generated the entire site in just a few minutes. I call it
"Magic Hands" because it looks like a really fast designer is
sitting in front of Gimp and doing all the photo work.

In this case I had a lot of control since the "rules" were Perl
code, and I was able to rebuild the entire site with new
content a few times with extremely little pain, which is where
it paid off. For example I could modify one entry in a data structure
which said that for example we had two more photos for the Singapore
franchise, and it would reinvent all the photo thumbnails, page
navigation, and so on.

The neat thing is that once you find it is really
easy to add something new, you do and you gain things you didn't
think were possible. For example in my project you click on
thumbnails to view images of hotel interiors, but we had given up
on the idea of highlighting the clicked thumbnail. I was able
to add this and even arrange the photos in a circle no matter
how many photos there were on each page. Also, since I
was able to refer to the parsed template files in the program,
I was able to change where a certain link would go for every
hotel just by changing a line of perl. I think you may find
similarly that it becomes easy to add campaigns.

In your case a straightforward html form could make an
easy-to-use interface which depending on your implementation
might sacrifice power for ease of use. There was a recommendation above about
looking at Outlook. Having helped someone recently clear his
Eudora files of spam, I'd take a look at Eudora's mail filtering
interface. Their downloadable manual shows how they let you
actually do a lot of really great things, even regexes. You
can also see an online tutorial and download Eudora itself.
Their use of different ways to get to the same screen was
confusing for me, but look at their documentation.. you will
need to write a manual.

As an aside, one company I knew some years ago, Intertrust, actually
was working on e-commerce rules (and I think Softbank is a
partner) but it looks like they've refocused on digital rights
management. I just mention them because I think they might
have some patents regarding specification of chains of resellers
but I think that is not something you need to worry about.

Anyway, look at Eudora's filters screen and see how it might
apply to your interface.
I'd recommend first rewriting your wishlist in pseudocode as
if-then statements. This will help you see datasets which
you will want to let your users maintain through separate
form interfaces. For example a list of preferred customer
classes might be one. Certainly the list of normal product
prices is another set of data that should be handled with
a separate interface. Then this rule-based form is for
is for special cases and business logic only.

You'll need to
make up a list of supported functions and handle internally
logic (not done in Eudora) to make sure for example that even if you
accumulate discounts you don't go beyond a certain percentage.
(I recently saw a shopping cart that let you earn money.)
This analysis will be needed even if you used someone else's system.

Another kind of interface which I find somewhat appealing
would be to require the user to type rules in Perl code
into an HTML text input field, and eval them in order.
This would have the advantage of allowing you or another
Perl person to do complicated things while they can do simple
things without much help, possibly just changing a couple of
numbers. You could put your error-checking logic into similar
statements as well and even modify them from the web.
One things you might consider is letting them
type english words like "greater than" instead of the mathematical
operators. You might even find some use in using a readline/prompt
based shell that would eval commands you type in against
test data to try out new rules.

Looking at it this way, your business logic starts to look like this..

Obviously you could make everything much more complex. This
example is going to need only three data queries and three
functions to change the order form.

Also it might be a good idea to let them type "if" first and
let them type "then" instead of a curly-bracketed block. Likewise
just an equals sign instead of "eq" perhaps. Maybe you could
add the keyword "matches" which would let them type an
alphanumeric string that could be used to match product or
customer class names. You could even teach your customer a
little SQL though maybe that's where angels fear to tread.. :)
This could be useful for lots of things,
please share your successes.

For inspiration, if not for direct usage, you might take a look at Jess. It's in Java, but the source is available. In particular, the language it uses has been tested and modified over time and may provide some ideas. Jess, like many other rules engines, implements the Rete algorithm, which seems to be the standard. There's also a (relatively) open process to make a standard API/library in Java for this.

I had a look at this package and was fairly impressed with it up until I reviewed the licencing for the package, which may present some hurdles to those looking at incorporating Jess by itself into their own development. In summary from the download page ...

Jess software, owned by Sandia National Laboratories, will be made available upon request at no cost to U.S. Federal Government Agencies for their own internal use. Sandia will also provide Jess to Universities, Academic Institutions, and other U.S. National Laboratories, for their own internal research and development, through a no cost, restricted R&D license. Any internal or commercial use of Jess requires a commercial license that can be negotiated as a running royalty or a fully paid up-front fee.

Our commercial license will grant your company, the LICENSEE, a nontransferable, nonexclusive right to use Jess Software to create derivative works by embedding Jess into your product(s) and to copy and distribute Jess software as embedded into your Product(s).

Note: Jess is not licensed under the GPL, the LPGL, the BSD license, or any other free software or open source license. Redistribution of the Jess source code under any free software or open source license is prohibited under this agreement.

How much time do you want to spend on the project? I bet you could build something pretty nice given a few weeks of work. Will it be worth it? Think carefully before answering. I have a hunch you've drawn yourself up a nice technical challenge that probably isn't justified by the required functionality.

Of course, it does sound like a lot of fun! I think I'd model a solution based on filter configuration systems ala Outlook. Basically a GUI that allows you to configure a series of rules that are then evaluated for each record. You'd have to pre-define a series of available functions and fields, but otherwise it could be very flexible.

Then you let your co-worker manipulate the rules in the database, using any tool s/he's comfortable with. Either a specific tool built by you or a generic database manipulation tool, all depending on your co-workers skills.

So your co-worker could make sure that for 'gizmo', the client must by 30 units before having a volume discount, but for 'thing' the client must buy 50 units. Furthermore, the volume discount for 'gizmo' is 20% whereas for 'thing' it's 23%. Etc.

Don't do it. I am currently working with a Java system called ATG Dynamo. It has a very complete rules-based system for promotions and discounts. It sucks.

Why does it suck? Because accounting for all possible kinds of discounts means you end up building a complex configuration language and a pretty heavy engine for interpreting it. It's way too complex for a non-tech person to comfortably work with, and thus it fails at its goal.

I suggest you write an interface for plugging in short pieces of code (maybe use the Strategy design pattern), and write these rules yourself in clean, well-commented perl. They will be much more efficient, and probably require less time to change if you have a competent perl programmer and a reasonable API.

Yeah, I agree. I've also used CLIPS on several projects. Also Prolog. These aren't easy systems for a non-programmer to use directly.

You can make CLIPS (or, IMO any rule-based system) easy to use like this in one dimension, but not the dimension you want. For example, I had a system for diagnosis according to DSM-IV criteria. It had a nice interface for users to enter new "facts", which the rules would then run against. But, allowing users to be able to add new rules (and preserve the existing dependencies and logic) would have been way more difficult (or impossible).

How about the "standard" way of solving this? From your description of the problem, it's exactly what OO programming is about. You create business objects in clean OO code, and give them methods that model the business rules. The methods only allow the various operations to go through if whatever criteria is met. This is a really clean way to code, and yeah - you need an OO programmer to maintain it. But, the problem itself requires it, I think.

I definitely agree with Perrin. The most effective, usable system
is going to be a framework that allows you to glue together concise,
targeted bits of code. You can't beat the power of Perl for scripting
complicated interactions between little bits of data.

We use XML::Comma for managing
web-site user profiles, mailing list systems, content archives,
etc. We haven't done quite this kind of shopping cart, but it's a
pretty good fit.

In Comma, you write document definitions that specify data
structures, constrain the contents of the parts of those structures,
and govern how data is saved in filesystem- and database-backed
storage. There's a pretty extensive API for manipulating both
individial documents and collections of docs.

You always hand-write the definitions -- that's the core exercise
of figuring out basic data structures and behaviors. Writing defs
feels like a kind of OO mating ritual: XML on one side, perl on the
other, and between the two of them a Class is born.

Most of our systems are built to assume completely tools-generated
docs (for various definitions of "tools"). You can write docs by hand,
and for your shopping cart system you might want to. For instance,
creating a new "item" in the product catalog could be the job for a
decent programmer who understands something about how the system is
used but doesn't have much knowledge of the
internals/architecture/subtleties.

Anyway, I threw together a bit of Comma stuff showing how you
might structure your widgets & gizmos example. Whether or not you
want to look more at Comma, it might provide some more food for
thought. There are a few example "item" and "customer" docs, followed
by defs for "item", "customer" and "cart".

Personally, I'd make the configuration language Perl code
that can be evaluated. I'd have this generated programmatically from an interface the user can understand.
I'd store the code in the database along with the product.
I'd also store enough of the rules that went into creating the Perl code in the database that the UI could show the
original rules for perusal. Finally, I'd have a field specifically to tell in what order the rules ar eto be applied.

I have a specific idea of how I'd do the rules entry
interface if you're interested...

From the rules listed in the my UI description, it's easy to generate code in the style of simple if statements. The specific ordering lets you do each one independent of the others, in the order specified by the user. The only thing this doesn't take into account in the simple manner of its design is to make the rules interact based on the results from the other rules. Even that should be possible with extra variables to be set and checked, but that's beyond the scope of this node and is left as an exercise for now.

Christopher E. Stith
Do not try to debug the program, for that is impossible. Instead, try only to realize the truth. There is no bug.
Then you will find that it is not the program that bends, but only the list of features.

Now, I love Guile (see Inline::Guile for proof) but why would you suggest it for this project? Guile is a Scheme interpreter. Scheme supports functional programming ala Lisp. It has nothing to do with rule-based systems.

Well, I said I'm not sure that guile is what he needs.
I suggested it because, believe it or not, we (the development team I'm working at) are currently looking into guile as a base for implementing a rule based system.
Rule based system can be (beautifully) implemented through Scheme. In the book Structure and Interpretation
of Computer Programs there is a nice implementation of a rule based system in Scheme <A HREF=http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-29.html#%_sec_4.4.1">here.
Thanks a lot for pointing me to the module. I will defently look into that.
-bserfaty

Ada Lovelace for the palindrome
Albert Einstein for having smelly feet
Alfred Nobel for his contribution to battlefield science
Burkhard Heim for providing the missing link between science and mysticism
Claude Shannnon for riding a unicycle at night at MIT
Donald Knuth for being such a great organist
Edward Teller for being the template for Dr. Strangelove
Edwin Hubble for pretending to be a pipe-smoking English gentleman
Erwin Schrödinger for cruelty to cats
Hedy Lamarr for weaponizing pianos
Hugh Everett for immortality, especially for cats
Isaac Newton for his occult studies
Kikunae Ikeda for discovering the secrets of soy sauce
Larry Wall for his website
Louis Camille Maillard for discovering why steaks taste good
Marie Curie for the shiny stuff
Nikola Tesla for the cool cars
Paul Dirac for speaking one word per hour when socializing
Richard Feynman for his bongo skills
Robert Oppenheimer for his in-depth knowledge of the Bhagavad Gita
Rusi P Taleyarkhan for Cold Fusion
Sigmund Freud for his Ménage ā trois
Theodor W Adorno for his contribution to the reception of jazz
Wilhelm Röntgen for the foundations of body scanners
Yulii Borisovich Khariton for the Tsar Bomba
Other (please explain why)