Difference between revisions of "Guide:Aspects"

From IokeWiki
Jump to: navigation, search
(correcting some mistakes)
Line 1: Line 1:
 +
=[http://uvetysudema.co.cc UNDER COSTRUCTION, PLEASE SEE THIS POST IN RESERVE COPY]=
 
= Aspects =
 
= Aspects =
  
Line 11: Line 12:
 
To create a Pointcut, call before, after or around on the receiver where the Pointcut should belong. The arguments specify what should be matched by the pointcut. You can see some examples of pointcuts here:
 
To create a Pointcut, call before, after or around on the receiver where the Pointcut should belong. The arguments specify what should be matched by the pointcut. You can see some examples of pointcuts here:
  
<source lang="ioke">; matches cells foo and bar
+
&lt;source lang=&quot;ioke&quot;&gt;; matches cells foo and bar
 
Text before(:foo, :bar)
 
Text before(:foo, :bar)
  
Line 34: Line 35:
  
 
; use a list to provide alternatives
 
; use a list to provide alternatives
X before(matching: [#/foo/, #/bar/])</source>
+
X before(matching: [#/foo/, #/bar/])&lt;/source&gt;
 
As you can see from these examples, the pointcuts can be fairly advanced and specific in what they match. The option to send in a block makes it possible to check any property while matching.
 
As you can see from these examples, the pointcuts can be fairly advanced and specific in what they match. The option to send in a block makes it possible to check any property while matching.
  
OK, once you have a Pointcut, there are three different methods you can call. These are &lt;&lt;, add, and remove!. The first one adds an unnamed advice, the second adds a named advice and the third one removes a named advice. Advice is any object that can be activated or called, so a method, a block, a macro or a syntax is fine.
+
OK, once you have a Pointcut, there are three different methods you can call. These are &amp;lt;&amp;lt;, add, and remove!. The first one adds an unnamed advice, the second adds a named advice and the third one removes a named advice. Advice is any object that can be activated or called, so a method, a block, a macro or a syntax is fine.
  
 
It's time to see what it looks like to add advice. Before-advice is the easiest kind. The important thing to remember is that the code will get the same arguments as the original call, which means it will also signal an error if the arguments doesn't match.
 
It's time to see what it looks like to add advice. Before-advice is the easiest kind. The important thing to remember is that the code will get the same arguments as the original call, which means it will also signal an error if the arguments doesn't match.
  
<source lang="ioke">X before(:foo) << method(x, "got #{x}" println)
+
&lt;source lang=&quot;ioke&quot;&gt;X before(:foo) &lt;&lt; method(x, &quot;got #{x}&quot; println)
X before(matching: :anyFromSelf) << macro(
+
X before(matching: :anyFromSelf) &lt;&lt; macro(
   "called #{call message name}" println)</source>
+
   &quot;called #{call message name}&quot; println)&lt;/source&gt;
 
This code doesn't do anything strange at all.
 
This code doesn't do anything strange at all.
  
 
Next up we have after-advice. The only difference here is that after-advice automatically gets a cell that is set to aspectResult, that can be used inside the method.
 
Next up we have after-advice. The only difference here is that after-advice automatically gets a cell that is set to aspectResult, that can be used inside the method.
  
<source lang="ioke">X after(:foo) << method(+args,
+
&lt;source lang=&quot;ioke&quot;&gt;X after(:foo) &lt;&lt; method(+args,
   "foo resulted in: #{aspectResult}" println)</source>
+
   &quot;foo resulted in: #{aspectResult}&quot; println)&lt;/source&gt;
 
Remember that the result value of before and after advice doesn't count. It is thrown away. But these methods can still affect things by side effects. They can also be used for validation. A condition signalled inside of an advice would have the same effect as if done in the method itself - namely interrupting the flow of control.
 
Remember that the result value of before and after advice doesn't count. It is thrown away. But these methods can still affect things by side effects. They can also be used for validation. A condition signalled inside of an advice would have the same effect as if done in the method itself - namely interrupting the flow of control.
  
 
The final advice is around advice. These are different in two ways. First, they get access to a cell called aspectCall, which can be used to invoke the real cell (and the next chain of advice, of course). The second difference is that the result of the around advice will be the return value from the cell. So, you can imagine the around-advice executing instead of the original code. If you forget to invoke it, the original cell won't be invoked at all.
 
The final advice is around advice. These are different in two ways. First, they get access to a cell called aspectCall, which can be used to invoke the real cell (and the next chain of advice, of course). The second difference is that the result of the around advice will be the return value from the cell. So, you can imagine the around-advice executing instead of the original code. If you forget to invoke it, the original cell won't be invoked at all.
  
<source lang="ioke">X around(:non_existant) << method(
+
&lt;source lang=&quot;ioke&quot;&gt;X around(:non_existant) &lt;&lt; method(
 
   42)
 
   42)
  
X around(:foo) << method(arg1, arg2,
+
X around(:foo) &lt;&lt; method(arg1, arg2,
   "before" println
+
   &quot;before&quot; println
 
   res = aspectCall(arg2, arg1)
 
   res = aspectCall(arg2, arg1)
   "got: #{res}" println
+
   &quot;got: #{res}&quot; println
   42)</source>
+
   42)&lt;/source&gt;
 
The first piece of code show that you can actually use an around advice around something that doesn't exist. But if you do call aspectCall inside of it, that will generate a NoSuchCell condition, of course. In the second example we first log some information, then invoke the original code with switched argument order, saves away the result, prints the result, and finally returns 42. There you can see most of the things that can be done inside of an around macro.
 
The first piece of code show that you can actually use an around advice around something that doesn't exist. But if you do call aspectCall inside of it, that will generate a NoSuchCell condition, of course. In the second example we first log some information, then invoke the original code with switched argument order, saves away the result, prints the result, and finally returns 42. There you can see most of the things that can be done inside of an around macro.
  
 
The aspect system in Ioke is implemented in Ioke itself, and is fairly small at the moment. The guiding principle behind it is that it shouldn't have an impact on code that doesn't use it. It is a highly useful feature that makes it possible to decompose code substantially. As an example of a common place for aspect usage is initialization. This is defined as an around-advice that looks like this:
 
The aspect system in Ioke is implemented in Ioke itself, and is fairly small at the moment. The guiding principle behind it is that it shouldn't have an impact on code that doesn't use it. It is a highly useful feature that makes it possible to decompose code substantially. As an example of a common place for aspect usage is initialization. This is defined as an around-advice that looks like this:
  
<source lang="ioke">Origin around(:mimic) << method(+rest, +:krest,  
+
&lt;source lang=&quot;ioke&quot;&gt;Origin around(:mimic) &lt;&lt; method(+rest, +:krest,  
 
   newMimic = aspectCall
 
   newMimic = aspectCall
 
   if(newMimic cell?(:initialize),
 
   if(newMimic cell?(:initialize),
 
     newMimic initialize(*rest, *krest))
 
     newMimic initialize(*rest, *krest))
   newMimic)</source>
+
   newMimic)&lt;/source&gt;
 
Note first that the around advice takes any kind of arguments, but doesn't send them along to mimic. Instead it checks if there is an initialize-cell, and in that case invokes it with the arguments given to the mimic call. It finally returns the original result.
 
Note first that the around advice takes any kind of arguments, but doesn't send them along to mimic. Instead it checks if there is an initialize-cell, and in that case invokes it with the arguments given to the mimic call. It finally returns the original result.

Revision as of 03:01, 24 November 2010

UNDER COSTRUCTION, PLEASE SEE THIS POST IN RESERVE COPY

Aspects

In many circumstances you might want to do several different things in a specific method. That generally goes against good software engineering, that says every piece of code should only have one responsibility. Aspect oriented programming tackles this by allowing you to slice code along a different dimension compared to the regular object oriented paradigm. Ioke has a small version of this, that allow you to add advice to cells. This advice can be before, after or around. All of them will get access to the arguments given in the original message, but after-advice also gets access to the value returned from the main cell. Finally, around-advice also gets the responsibility to invoke the original cell, so an around-advice could choose to not invoke it at all, or it could invoke it with different arguments, or maybe invoke it several times.

Ioke currently only allows you to add or remove advice. To be able to remove advice, you need to name it when adding it. Unnamed advice can not be removed. Advice can be wrapped. It will be applied in an outside-in approach, where the most recently added advice will execute first.

The return value of before and after advice doesn't matter, but the return value of around-advice will be interpreted as the new return value of the message send.

To manipulate advice in Ioke, you need to describe what to manipulate. This description will return a Pointcut, that can then be used to inject advice at that point. Pointcuts can be defined in several ways. The easiest is to define it in terms of a cell name. Using this approach is the only way to define advice for non-existent cells.

To create a Pointcut, call before, after or around on the receiver where the Pointcut should belong. The arguments specify what should be matched by the pointcut. You can see some examples of pointcuts here:

<source lang="ioke">; matches cells foo and bar Text before(:foo, :bar)

X = Origin mimic

will not match anything

X around(matching: :anyFromSelf)

X foo = 123

will now match foo

X around(matching: :anyFromSelf)

any cell name matching the regexp

X after(matching: #/foo/)

matches any at all, except foo

X before(matching: :any, except: :foo)

matches any at all, except anything that matches foo

X before(matching: :any, except: #/foo/)

use a block for matching

X around(matching: fn(x, x == :blurg))

use a list to provide alternatives

X before(matching: [#/foo/, #/bar/])</source> As you can see from these examples, the pointcuts can be fairly advanced and specific in what they match. The option to send in a block makes it possible to check any property while matching.

OK, once you have a Pointcut, there are three different methods you can call. These are &lt;&lt;, add, and remove!. The first one adds an unnamed advice, the second adds a named advice and the third one removes a named advice. Advice is any object that can be activated or called, so a method, a block, a macro or a syntax is fine.

It's time to see what it looks like to add advice. Before-advice is the easiest kind. The important thing to remember is that the code will get the same arguments as the original call, which means it will also signal an error if the arguments doesn't match.

<source lang="ioke">X before(:foo) << method(x, "got #{x}" println) X before(matching: :anyFromSelf) << macro(

 "called #{call message name}" println)</source>

This code doesn't do anything strange at all.

Next up we have after-advice. The only difference here is that after-advice automatically gets a cell that is set to aspectResult, that can be used inside the method.

<source lang="ioke">X after(:foo) << method(+args,

 "foo resulted in: #{aspectResult}" println)</source>

Remember that the result value of before and after advice doesn't count. It is thrown away. But these methods can still affect things by side effects. They can also be used for validation. A condition signalled inside of an advice would have the same effect as if done in the method itself - namely interrupting the flow of control.

The final advice is around advice. These are different in two ways. First, they get access to a cell called aspectCall, which can be used to invoke the real cell (and the next chain of advice, of course). The second difference is that the result of the around advice will be the return value from the cell. So, you can imagine the around-advice executing instead of the original code. If you forget to invoke it, the original cell won't be invoked at all.

<source lang="ioke">X around(:non_existant) << method(

 42)

X around(:foo) << method(arg1, arg2,

 "before" println
 res = aspectCall(arg2, arg1)
 "got: #{res}" println
 42)</source>

The first piece of code show that you can actually use an around advice around something that doesn't exist. But if you do call aspectCall inside of it, that will generate a NoSuchCell condition, of course. In the second example we first log some information, then invoke the original code with switched argument order, saves away the result, prints the result, and finally returns 42. There you can see most of the things that can be done inside of an around macro.

The aspect system in Ioke is implemented in Ioke itself, and is fairly small at the moment. The guiding principle behind it is that it shouldn't have an impact on code that doesn't use it. It is a highly useful feature that makes it possible to decompose code substantially. As an example of a common place for aspect usage is initialization. This is defined as an around-advice that looks like this:

<source lang="ioke">Origin around(:mimic) << method(+rest, +:krest,

 newMimic = aspectCall
 if(newMimic cell?(:initialize),
   newMimic initialize(*rest, *krest))
 newMimic)</source>

Note first that the around advice takes any kind of arguments, but doesn't send them along to mimic. Instead it checks if there is an initialize-cell, and in that case invokes it with the arguments given to the mimic call. It finally returns the original result.