Search code examples
cachingreflectionsmalltalkpharo

Caching the result of all methods


I have a class which is essentially a collection of methods for some data transformations. In another words I have some data in my files and I use a few different code snippets to transform the textual data into something that I can easily query.

Now the methods often reuse each-other and as the core data is changing I'd like to simply cache the results of each method, for the speed reasons.

I don't want to change each method by adding:

^ methodsCache ifNil: [ methodsCache := "compute" ]

I want to use the power of Pharo reflection to accomplish my task without touching much of code.

One idea that I had is if I can run some code before each method, thing I can either return a cached value or continue the execution of the method and cache it's result


Solution

  • You could use the Reflectivity framework to add pre and post meta links to your methods. A link could check a cache before execution transparently.

    link := MetaLink new
        metaObject: self;
        selector: #cachedExecute:;
        arguments: #(selector);
        control: #before.
    (MyClass>>#myMethodSelector) ast link: link.
    

    This code will install a meta link that sends #cachedExecute: to a MyClass object with the argument #myMethodSelector. The link is installed on the first AST node of the compiled method (of that same method selector, but could be on another method). The #control: message ensures that the link will be executed before the AST node is executed.

    You can of course install multiple meta links that influence each other.

    Note that in the above example you must not send the same message (#myMethodSelector) again inside of the #cachedExecute: method since you'd end up in a loop.

    Update There's actually an error in the code above (now fixed). The #arguments: message takes a list of symbols that define the parameters of the method specified via #selector:. Those arguments will be reified from the context. To pass the method selector you's use the #selector reification, for the method context the #context reification and for method arguments #arguments. To see which reifications are available, look at the #key on the class side of the subclasses of RFReification.