Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to have additional state for an interceptor without falling back to globals #15

Open
mkustermann opened this issue Apr 18, 2015 · 1 comment
Labels

Comments

@mkustermann
Copy link

If an interceptor needs more state than the target member (or state of a different type) - it looks like with the current proposal - one needs to fall back using globals (e.g. global Maps) (possibly indexed by target object and/or interceptor arguments). The @memoize interceptor is an example of this (e.g. @memorize fib(a,b) ). Is this correct?

In order to guarantee that the additional state has the same lifetime as the target itself (i.e. the state should be GCed at the same time), it becomes more complicated, since one may need to use weak maps, ...

It would be nice if an interceptor could introduce new state without relying on globals.

@sigmundch
Copy link
Owner

I couldn't agree more. I'm trying to think something that would be well encapsulated and feasible to implement.

In today's proposal we automatically move the original member to be private, and make the interceptor the public member. We create the private member automatically because it is a common case that you'll need it, but I'm considering that we should let the interceptor declare what it needs instead. In particular some other options we could have are:

  • add a private field for an actual instance of the interceptor. Semantically this is like creating an object on every intercepted member, but I hope we can do this more efficiently in our implementations.
  • let the interceptor declare what members need to be added. But give the interceptor a special namespace, so that there are no conflicts with existing members, or members from other instantiations of the interceptor.

Both cases we can no longer think of interceptors as plain const expressions, but we still know statically what the shape of the program is. This might be easier to explain with an example.

Consider that we now declare memoization as follows:

class Memoize implements InvokeInterceptor {
  Map<Invocation, dynamic> _memoizedResults;
  Member _member;
  Memoize(this._member);

  invoke(target, invocation) =>
    _memoizedResults.putIfAbsent(invocation, () => _member.invoke(t, invocation));
}

And consider that we write the interceptor using a special syntax like:

class A {
   fibonacci(n) with Memoize => ...;
   factorial(n) with Memoize => ...;
}

Under the first idea, the semantics would be equivalent to having:

class A {
   final _fibonacci_interceptor = new _Memoize(_fibonacciMember);
   fibonacci(n)  => _fibonacci_interceptor.invoke(...);

   final _factorial_interceptor = new _Memoize(_factorialMember);
   factorial(n) => _factorial_interceptor.invoke(...);
}

Each interceptor has it's own storage, and it's lifetime matches that of the containing object.

The semantics of the second idea is that applying the interceptor means to copy every field in the interceptor class with a unique name. This second idea is sort of like inlining directly in the containing object the interceptor instances from the first idea. Sort of like a fine-grain "mixin". In terms of our example, the second idea would look like this:

class A {
  Map<Invocation, dynamic> _memoizedResults_fibonacci;
  Member _member_fibonnaci;
  _fibonnaci(n) => ...;

  fibonnaci(n) =>  _memoizedResults_fibonnacci.putIfAbsent([n],
      () => _member_fibonacci.invoke(t, [n]));

  Map<Invocation, dynamic> _memoizedResults_factorial;
  Member _member_factorial;
  _factorial(n) => ...;
  factorial(n) =>  _memoizedResults_factorial.putIfAbsent([n],
      () => _member_factorial.invoke(t, [n]));
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants