Aspect oriented programming: Difference between revisions

m
m (Looks like a draft task)
m (→‎{{header|Wren}}: Minor tidy)
 
(39 intermediate revisions by 15 users not shown)
Line 1:
{{clarify task}}{{draft task}}
;Background
The main goal of Aspect Oriented Programming (AOP) is to keep all the code relating to a single feature in one module of code.
 
Line 22 ⟶ 21:
The task is to describe or show how, or to what extent, a given programming language implements, or is able to implement or simulate, Aspect Oriented Programming.
 
=={{header|Java6502 Assembly}}==
The easiest way to do this is by putting every related function into the same file. One important thing to remember is that in 6502 (and most other assembly languages for that matter, unless linkers are involved), the order in which your code is placed implies its memory location. This is not the case in other languages, not even in [[C]].
<syntaxhighlight lang="6502asm">;Basic_Functions.asm
SwapXY:
pha
txa
pha
tya
 
tax
pla
tay
pla
rts
 
NewLine: ;the above function will be stored in memory above this one.
lda #13
jmp $FFD2 ;PrintChar on Commodore 64</syntaxhighlight>
 
This is somewhat relevant because it lets you abuse fallthrough. If you have a special case of a particular function that you use often, you can easily make that special case its own function by having it load those specific parameters and then "falling through" into the function you're actually running. This helps to reduce call/return penalties while keeping your relevant pieces of code together. Unfortunately, you can only do this once per function, for obvious reasons.
 
<syntaxhighlight lang="6502asm">NewLine:
LDA #10 ;linefeed
;the PrintChar function is literally below this line, both in a source code sense and a memory layout sense.
 
PrintChar:
;input: A = the ascii code you wish to print to the screen.
;
; unimplemented because it's dependent on the video hardware but you get the idea</syntaxhighlight>
 
=={{header|Ada}}==
The primary unit of modularity in Ada is the package. Ada provides the ability to create child packages with visibility into portions of their parent package without modifying the parent package. The child package can provided extended capabilities based upon the data types, data elements and subprograms defined in the parent package.
 
Example parent package specification:
<syntaxhighlight lang="ada">package parent is
function Add2 (X : in Integer) return Integer;
end parent;</syntaxhighlight>
Example parent package body:
<syntaxhighlight lang="ada">package body parent is
 
function Add2 (X : in Integer) return Integer is
begin
return X + 2;
end Add2;
 
end parent;</syntaxhighlight>
Example child package specification:
<syntaxhighlight lang="ada">package parent.child is
function Add2 (X : in Integer) return Integer;
end parent.child;</syntaxhighlight>
Example child package specification:
<syntaxhighlight lang="ada">with Ada.Text_IO; use Ada.Text_IO;
 
package body parent.child is
 
function Add2 (X : in Integer) return Integer is
begin
Put_Line ("Added 2 to " & X'Image);
return parent.Add2 (X);
end Add2;
 
end parent.child;</syntaxhighlight>
The following program demonstrates calling both the Add2 function from the parent package and calling the Add2 function from the child package. The child package has visibility to the public portion of the parent package and can therefore call the parent's Add2 function directly.
<syntaxhighlight lang="ada">with parent.child;
with Ada.Text_IO; use Ada.Text_IO;
 
procedure Main is
Num : Integer := 5;
Result : Integer;
begin
Put_Line ("Calling parent Add2 function:");
Result := parent.Add2 (Num);
Put_Line ("Result : " & Result'Image);
New_Line;
Put_Line ("Calling parent.child Add2 function");
Result := parent.child.Add2 (Num);
Put_Line ("Result : " & Result'Image);
end Main;</syntaxhighlight>
{{output}}
<pre>
Calling parent Add2 function:
Result : 7
 
Calling parent.child Add2 function
Java has an aspect oriented programming library called AspectJ. Aspects can create entry and exit intercepts on normal methods. In aspect language, features are called cross-cutting concerns.
Added 2 to 5
Result : 7
</pre>
 
=={{header|C}}==
Line 34 ⟶ 118:
When a new feature introduces code scattered throughout the program, we can relate all the code together using a define and ifdefs.
 
<syntaxhighlight lang="c">
<lang c>
#define MY_NEW_FEATURE_ENABLED
 
Line 48 ⟶ 132:
close_my_new_feature();
#endif
</syntaxhighlight>
</lang>
 
As well as allowing us to enable or disable the feature at compile time, this also provides a way to find all the relevant code easily, by searching for the variable name.
Line 58 ⟶ 142:
An alternative macro method can be used in C, which is shorter for one-liners.
 
<syntaxhighlight lang="c">
<lang c>
/* Enable logging: */
/* #define LOG(x) printf("%s\n",x); */
Line 69 ⟶ 153:
 
...
</syntaxhighlight>
</lang>
 
===Using function pointers===
Line 77 ⟶ 161:
Here is a typical layout:
 
<langsyntaxhighlight lang="c">struct object {
struct object_operations *ops;
int member;
Line 84 ⟶ 168:
struct object_operations {
void (*frob_member)(struct object *obj, int how);
};</langsyntaxhighlight>
 
In this example, an object is constructed as an instance of <code>struct object</code> and the <code>ops</code> field is filled in with a pointer to an operations table of type <code>struct object_operations</code>. The object is usually dynamically allocated, but the operations table is often statically allocated, and the function pointers are statically initialized.
Line 90 ⟶ 174:
A call to the <code>frob_member</code> method, if coded by hand without the help of any macros or wrapper functions, would look like this:
 
<langsyntaxhighlight lang="c">pobj->frob_member(pobj, 42);</langsyntaxhighlight>
 
This representation opens the door to various possibilities. To gain control over all of the calls to an object, all we have to do is replace its <code>ops</code> pointer with a pointer to another operations structure of the same type, but with different function pointers.
Line 103 ⟶ 187:
 
However, the claims that "[i]t turned out that the pointcut functionality of AspectL does not make a lot of sense in Common Lisp, and the support for dynamically scoped generic functions has been replaced with much better mechanisms in [[http://common-lisp.net/project/closer/contextl.html ContextL]]."
 
=={{header|F_Sharp|F#}}==
 
[http://eprints.bbk.ac.uk/20835/1/csci2017.pdf An Aspect-Oriented Framework for F#]
 
=={{header|FreeBASIC}}==
FreeBASIC does not have any specific support for AOP.
 
=={{header|Go}}==
 
Go does not have any specific support for AOP in either the language itself or its standard library and there appears to be little interest in adding any.
 
Nevertheless, there are at least three third party libraries for adding AOP functionality to Go, including [https://github.com/gogap/aop this one] which includes a code example. However, none of them have progressed beyond 'alpha' status and, as there have been no recent commits, development may have stalled.
 
=={{header|J}}==
 
In as much as I am unable to see the differences between functional programming and aspect oriented programming (they are just that stealthy, from my point of view), I'll have to say that J is as aspect oriented as the capabilities of the programmer.
 
For example, the phrase <code>([echo)</code> prints a value to the screen but is otherwise an identity function. (We could replace <code>echo</code> with an arbitrarily elaborate logging statement if we wished.) And, we can insert this statement into the middle of an expression to gain insight into its progress.
 
Thus, <syntaxhighlight lang="text"> <-/p:i.5 NB. a mysterious expression
┌─┐
│8│
└─┘
<-/p:([echo)i.5 NB. show the right argument to p:
0 1 2 3 4
┌─┐
│8│
└─┘
<-/([echo)p:i.5 NB. show the right argument to -/
2 3 5 7 11
┌─┐
│8│
└─┘
<(-([echo))/p:i.5 NB. show the right arguments to -
11
_4
9
_6
┌─┐
│8│
└─┘
<(-~([echo))~/p:i.5 NB. show the left arguments to -
7
5
3
2
┌─┐
│8│
└─┘
<-&([echo)/p:i.5 NB. show both the right and left arguments to -
11
7
_4
5
9
3
_6
2
┌─┐
│8│
└─┘
<([echo)-/p:i.5 NB. show the right argument to <
8
┌─┐
│8│
└─┘</syntaxhighlight>
 
Or, generally speaking, J provides us with the ability to wrap arbitrary statements with information gathering statements while allowing the original expression to proceed. (And, if we enable debugging, this information gathering can examine the surrounding context, gathering and reporting information on the calling environment, inspecting the local symbol table, the names of routines, the defining script(s), etc.)
 
=={{header|Java}}==
 
Java has an aspect oriented programming library called [https://www.eclipse.org/aspectj/ AspectJ]. Aspects can create entry and exit intercepts on normal methods. In aspect language, features are called cross-cutting concerns.
 
=={{header|JavaScript}}==
 
Bemson's [https://github.com/bemson/Flow/wiki/ Flow library] introduces an aspect-like framework for JavaScript.
 
=={{header|Julia}}==
Several of Julia's graphics frameworks use the idea of a backend for graphics, where the graphics module wraps and extends a lower level graphics framework. The use of one module to wrap another captures most of the ways in which aspect programming seems to be used. As an example, here we add logging to a module. Users of the Adder module can simply import the LogAspectAdder module instead of the Adder module to add logging to the functions of the Adder class.
<syntaxhighlight lang="julia">module Adder
exports add2
 
function add2(x)
return x + 2
end
end
 
module LogAspectAdder
exports add2
using Adder
const logio = [stdout]
function log(s, io=logio[1])
println(io, now(), " ", s)
end
 
function add2(x)
log("added 2 to $x")
Adder.add2(x)
end
end
</syntaxhighlight>
 
=={{header|Kotlin}}==
The most popular framework for aspect oriented programming in Kotlin JVM is probably [https://en.wikipedia.org/wiki/Spring_Framework#Aspect-oriented_programming_framework Spring AOP] which is simpler and seems to have less issues than AspectJ, described in the Java entry.
 
However, one issue Spring AOP does have is that it expects all classes to be open to inheritance whereas in Kotlin the default is for classes to be closed to inheritance though this can be changed by use of the 'open' modifier. To deal with this, JetBrains provide an 'all open' compiler plug-in which makes all classes annotated with specific annotations (such as @Configuration or @Service) open without the need for the 'open' modifier.
 
=={{header|M2000 Interpreter}}==
M2000 written to be an interpreter which execute source code without using something, such a second interpretation or compilation at execution stage. We get what we have program, without changes. At execution time source consumed, and we can watch that using Test statement, which opens a Control form, where we can step through execution, we can execute code, change values in variables, also we can edit code - not for code already in consuming stage. Test statement works as break-point too, and we can set a name to stop execution when a test statement use it, otherwise we see code executed in low speed, as control form stay opened. Because this interpreter uses Events for forms, there is a way to hook an event object between a form and event service functions, to intercept the events and parameters. This hook object is a same object as the "automatic" one, but need code to send messages to final event service functions. Critical events, which get feedback from variables by reference, can't be hooked.
 
Except of the watch, seek and change job, we can use Profiler to start high resolution timer and we can get values using Timecount. Also there is a simple way to have log files in temporary folder, using Text statement. A list statement can be used to show a list of variables to screen or exported to file. Because of dynamic addition of modules/functions, there is a module/function list which can watch using Modules ?. Also we can watch threads using Threads as for numbers of threads, and in Control form (we can step through execution including threads)
 
There is no way to debug machine code (we can write and execute machine code in memory buffers for Code, which code can't be altered, we use for data only another buffer(s))
 
=={{header|Perl}}==
The CPAN module [https://metacpan.org/pod/Aspect Aspect] brings features of AOP to Perl. From the documention:
 
The Perl Aspect module tries to closely follow the terminology of the basic Java AspectJ project wherever possible and reasonable.
 
However due to the dynamic nature of the Perl language, several AspectJ features are useless for us: exception softening, mixin support, out-of-class method declarations, annotations, and others.
 
Currently the Perl Aspect module is focused exclusively on subroutine matching and wrapping.
 
It allows you to select collections of subroutines and conditions using a flexible pointcut language, and modify their behavior in any way you want.
 
In addition, where the Java implementation of Aspect-Oriented Programming is limited to concepts expressable at compile time, the more fluid nature of Perl means that the Aspect module can weave in aspect code at run-time. Pointcuts in Perl can also take advantage of run-time information and Perl-specific features like closures to implement more sophisticated pointcuts than are possible in Java.
 
This allows the Perl implementation of Aspect-Oriented Programming to be stateful and adaptive in a way that Java cannot (although the added power can come with a significant speed cost if not used carefully).
 
=={{header|Phix}}==
{{trans|Go}}
Phix does not have any specific support for AOP in either the language itself or its standard library and there appears to be little interest in adding any.
 
(that was my idea of a joke, btw, and not a dig at Go but just a childish and silly use of "translation of")
 
Actually, there is one way to instrument (specific) calls without modifying the existing code. Suppose you have somelib.e, containing:
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">global</span> <span style="color: #008080;">procedure</span> <span style="color: #000000;">saysomething</span><span style="color: #0000FF;">()</span>
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"something\n"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<!--</syntaxhighlight>-->
Then write wraplib.e as follows:
<!--<syntaxhighlight lang="phix">-->
<span style="color: #008080;">without</span> <span style="color: #008080;">js</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">somelib</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span> <span style="color: #008080;">as</span> <span style="color: #000000;">somelib</span>
<span style="color: #008080;">global</span> <span style="color: #008080;">procedure</span> <span style="color: #000000;">saysomething</span><span style="color: #0000FF;">()</span>
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"wrapthing\n"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">somelib</span><span style="color: #0000FF;">:</span><span style="color: #000000;">saysomething</span><span style="color: #0000FF;">()</span>
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"thingwrap\n"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<!--</syntaxhighlight>-->
And replace all (existing) "include somelib.e" with "include wraplib.e". Hopefully there will be (and usually there is) only one.
 
What this won't do is instrument internal calls to internal routines, at least not without modifying somelib.e, nor will it work for
things not in separate files, nor many of the builtins - specifically it should be possible for most of builtins\, but not possible
for any of builtins\VM\, though doubtless a few cross over.
 
Personally, while I might begrudgingly accept that sort of thing for the occasional quick 'n dirty, or a temporary debug aid, I am strongly opposed to it being permanent, and instead strongly believe '''code should do what it says it does''', no more and no less. For easy toggling, my own code tends to be littered with things like:
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">global</span> <span style="color: #008080;">constant</span> <span style="color: #000000;">NEWFEATURE</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">true</span>
<span style="color: #0000FF;">...</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">NEWFEATURE</span> <span style="color: #008080;">then</span> <span style="color: #0000FF;">...</span>
<!--</syntaxhighlight>-->
I also actually favour explicit shims, almost exactly what the task is asking for a way to avoid doing, such as
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">function</span> <span style="color: #000000;">my_length</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">return</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<!--</syntaxhighlight>-->
and manually edit every single call, again so that any future (/temporary) changes are quick, easy & obvious.<br>
Also note the latter is compatible with p2js, whereas namespaces are not.
 
=={{header|Prolog}}==
Phil Hargett [https://github.com/hargettp/aop/ AOP library] introduces an aspect-like framework for Prolog.
 
AOP for Prolog is also introduced on [https://bigzaphod.github.io/Whirl/dma/docs/aspects/aspects-man.html this web page].
 
=={{header|Python}}==
Python has special syntax for [http://legacy.python.org/dev/peps/pep-0318/ decorators] acting on functions and methods, as well as [http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python metaclasses].
 
=={{header|Raku}}==
(formerly Perl 6)
 
All methods in Raku are really just routines under the control of the meta-object protocol for purposes of dispatch, so there are several ways to subvert the system in order to deal with aspects. First, you could intercept the MOP calls, or you could install alternate metaclasses. More likely, since methods are really just routines in disguise, and since a routine can be wrapped (in place) in an outer routine via the <tt>.wrap</tt> method, you'd just wrap all the methods and other routines that share a cross-cutting concern.
 
Note that the optimizer is free to assume routines are immutable after CHECK time (the end of compilation), so while you can always wrap routines during compile time, you might need a "<tt>use soft</tt>" declaration or some such to enable wrapping at run time, that is, in order to keep considering the routine to be mutable. (Only code doing the Routine role is ever mutable in Raku—bare blocks and other lambdas are considered immutable from the start.) Note that keeping routines mutable is likely to pessimize spots where the optimizer might otherwise be able to inline the code.
 
Of course, you can also simply redefine Raku on the fly to be any language you want in the current lexical scope, so you are really only limited by your imagination in how you wish to express these "aspects", whatever you might mean by that term…
 
=={{header|Scala}}==
Jones Bonér introduces AOP for Scala in [http://jonasboner.com/aop-style-mixin-composition-stacks-in-scala/ this blog].
==New Page Notes==
 
=={{header|Tcl}}==
Tcl's <code>trace</code> command enables much AOP-like functionality, with variable traces allowing interception of accesses to variables, and command traces allowing interception of executions of procedures, etc. In addition, TclOO (Tcl's <!--main--> object system) also supports filter methods, which wrap around invocations of methods of an object (or all methods of a class) as a generalisation of before, after and around advice. This works well when combined with a mixin, allowing interception to be applied on a per-instance basis if desired.
 
For example:
<syntaxhighlight lang="tcl">oo::class create InterceptAspect {
filter <methodCalled>
method <methodCalled> args {
puts "[self] - called '[self target]' with '$args'"
set result [next {*}$args]
puts "[self] - result was '$result'"
return $result
}
}
 
oo::class create Example {
method calculate {a b c} {
return [expr {$a**3 + $b**2 + $c}]
}
}
 
Example create xmpl
puts ">>[xmpl calculate 2 3 5]<<"
oo::objdefine xmpl mixin InterceptAspect
puts ">>[xmpl calculate 2 3 5]<<"</syntaxhighlight>
{{out}}
>>22<<
::xmpl - called '::Example calculate' with '2 3 5'
::xmpl - result was '22'
>>22<<
 
=={{header|Wren}}==
Wren has no support for AOP as such, either built-in or (AFAIK) via third parties. Nor does it have mixins.
 
However, our simple but flexible module system - a module is just a Wren source code file - makes it easy to wrap one class inside another in a similar fashion to the Julia example.
 
This enables us to add functionality to a class without interfering with its source code.
 
Notice also that a client can still import the ''wrapped'' class via the ''wrapper'' module which enables it to call ''unwrapped'' methods without the need to import the ''wrapped'' module as well. To expand the Julia example a little:
 
<syntaxhighlight lang="wren">/* adder.wren */
 
class Adder {
static add2(x) { x + 2 }
 
static mul2(x) { x * 2 }
}</syntaxhighlight>
 
<syntaxhighlight lang="wren">/* logAspectAdder.wren */
 
import "./adder" for Adder
 
var Start = System.clock // initialize timer for logging
 
class LogAspectAdder {
static log(s) {
var elapsed = ((System.clock - Start) * 1e6).round
System.print("After %(elapsed) μs : %(s)")
}
 
static add2(x) {
log("added 2 to %(x)")
return Adder.add2(x)
}
}</syntaxhighlight>
 
<syntaxhighlight lang="wren">/* adderClient.wren */
 
import "./logAspectAdder" for LogAspectAdder, Adder
 
var a = LogAspectAdder.add2(3)
var m = Adder.mul2(4)
System.print("3 + 2 = %(a)") // logged
System.print("4 * 2 = %(m)") // not logged</syntaxhighlight>
 
{{out}}
Running the client, we get:
<pre>
After 44 μs : added 2 to 3
3 + 2 = 5
4 * 2 = 8
</pre>
 
{{omit from|360 Assembly}}
I may have described a feature-oriented programming paradigm, or something related to modular programming. If so please feel free to rename or refactor the page!
{{omit from|6502 Assembly}}
{{omit from|8051 Assembly}}
{{omit from|8080 Assembly}}
{{omit from|8086 Assembly}}
{{omit from|68000 Assembly}}
{{omit from|AArch64 Assembly}}
{{omit from|ARM Assembly}}
{{omit from|MIPS Assembly}}
{{omit from|x86 Assembly}}
{{omit from|Z80 Assembly}}
9,476

edits