Re: Re: PEAK-rules question
PJ Eby <pje-Wh6+Hckhi6HFNGf7iClzIwC/[email protected]> Mon, 2 Sep 2013 20:32:58 -0400
| Newsgroups | gmane.comp.python.peak |
|---|---|
| Message-ID | <CALeMXf53Xc6sv2b9E9JZHtd=5WnurOLRqcdK8kVggwWkvScn0w@mail.gmail.com> |
On Sun, Sep 1, 2013 at 4:07 PM, S=E9bastien de Menten <[email protected]>= wrote: > Hello, > > After some research on the topic, I found a simple way to do what i wante= d > with macropy. > > I define the following macro in a module > > # macro_module.py > from macropy.core.macros import Macros, Str, unparse > > macros =3D Macros() > > @macros.expr > def s(tree, **kw): > return Str(unparse(tree)) > #=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > > and then use it as: > > # test.py > from macro_module import macros, s > from peak.rules import abstract, when > > @abstract > def foo(a,b): > pass > foo.when =3D lambda condition: when(foo, condition) #<=3D=3D this could= be > better implemented in the abstract decorator Better still might be to implement @when() and other such APIs as macros that transform just the second argument; that way your code would not need the extra "s" function. Still, it's definitely a way around the IDE issues, and doesn't suffer from the usual issues of access to source code (I assume, anyway). > For my second question, explaining the real domain space would need pages= so > I must find a 'to-the-point" analogy that captures the gist of the proble= m. > So here is one that does not use domain-specific terms but maps 100% with > the problem. > I have different kind of cars (pickup, sedans, convertible, etc) which ha= ve > different transmissions (4x4, traction, propulsion) and different tires (= all > season, snow, high performance, etc) and all combinations of Car x > Transmission x Tires are possible. > Now the generic functions I want to write are like : > - "optimal_acceleration(car, weather)" > - "maintenance_planning(car)" > - "customer_match(car, customer_profile)" > These functions may depend on the Car(Transmission, Tire) combination and > while all may not be supported, I want to be able to add support for any = of > them. Moreover, if a user adds a new kind of transmission, or car, or tir= e, > I want to be able to add the functions for these new combinations (if the= y > make sense ... otherwise, an exception NoApplicableMethods is perfect ! Sounds just like what PEAK-Rules is intended for. One thing you might want to watch out for in de-structuring rules like this (ones that depend on attributes of a parameter), is to remember that PEAK-Rules generally applies tests to parameters before testing attributes. This doesn't affect rule *precedence* (as far as what's most specific), but it does affect *evaluation order*. For example, if all your rules for a given function read in this order: isinstance(car.tires, Foo) and isinstance(car.transmission, Bar) That is, if they all check the tires before checking the transmission, then PEAK-Rules will conservatively assume that it's not safe to access the transmission attribute until after the tires are checked. This limits the shapes that the dispatch tree will be built in, even though it doesn't affect the answers given. Under some circumstances, this might create larger or slower dispatch trees. In contrast, tests based directly on parameters are assumed to be independent of each other (e.g. "param1>1 and param2>2" is assumed to allow checking either condition first) and so can be used at any level of the dispatch tree, and so the builder is free to use whatever shape works best for that subtree (at a cost of a little more setup time to decide which shape is best.) I guess what I'm trying to say is that if you have a relatively flat rule structure, based mainly on car, car.tires, and car.transmission, you may find it useful to make the tires and transmissions direct parameters, or else to deliberately vary the order they appear in your rules, so that the rule system will have maximum freedom to construct dispatch trees. I wouldn't even bother to mention this, except that from your description I don't have any way to know just how complex your dispatch trees will be. In general, I find that generic functions tend to follow one of two patterns: 1. "Registry" functions -- not heavily dependent on predicates, more dependent on types of a few key parameters, relatively simple rules with occasional extra criteria 2. "Pattern matching" or "compiler" functions -- while one or two parameter types may be involved, the bulk of the rules are predicates, often deeply nested predicates on component objects; often found in compilers or compiler-like systems (such as PEAK-Rules itself) to pattern-match subtrees with complex conditions. In pattern #2, evaluation order may be rather important, but the tree size is fairly limited by the fact that most of the actual predicates exist for only a few rules. In pattern #1, the bulk of the indexing is for that handful of parameters, so as long as they're freely-orderable (i.e., can be tested independently), you can't get a blowup of tree size. (And in pattern #1, the tests are usually done directly on the parameters, not on attributes or methods or formulas using the parameters.) >From your vague description, I can't tell if your use case is more like #1 or #2, or some sort of hybrid. If it's basically #1 but using lots of independent attribute-level checks, and you have very large rulesets, then you may want to promote the attributes to parameters (or access those attributes in different order some of the time), in order to allow PEAK-Rules full tree reordering freedom. It will not affect the answers you get, only memory usage (and possibly speed). Even then, it's unlikely to do it in the kinds of uses I'm familiar with, but since I'm not familiar with your case, I am being extra-thorough and cautious. ;-)