www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - facets

reply Kevin Bealer <Kevin_member pathlink.com> writes:
I like the on_scope_exit() and friends, by the way.

But it got me thinking about an idea I had.  It's a bit like aspect
oriented programming, but that is usually done with classes.  This is
a function based one.  The name I'm thinking of for this is "facet
programming", sort of suggesting "function based aspects".

The idea is to have semi-independent functions that can be "zipped"
together at specified points to combine different implementations of
sub-functionalities, or to add and remove those elements.

Imagine that I have one function that reads lines of data from a file,
another that reads from a user interface, and I want to process the
data one line at a time.  I can also have several "consumers" of the
data.  Here I show two of each.

However, the real power would be in writing a conceptual "script" for
some sequence of interactions (this is represented as "A: B: C:"
below).  This script would allow dozens or hundreds of classes to
interact at controlled places.  Currently this requires writing a
master function that knows everything about each of the interactions
between the parts, for all the parts.

The word "facet" defines a facet of the program; "together" indicates
points that all functions need to reach before any can proceed.  The
modifier "other" means "find this variable name in one of the other
aspects instead of locally".  This is sort of like "extern" in C.

: facet void read_from_file(char[] fname)
: {
:    together A:
:       LineReader lr = new LineReader(name);
:    
:    bool done = lr.eof();
:
:    while(! done) {
:       char[] line = lr.readLine();
:       done = lr.eof();
:    together B:
:    }
:   
:    together C:
:       lr.close();
: }

: facet void parse_lines(char[][] lines)
: {
:    together A:
:
:    bool done = ! lines.length;
:
:    while(! done) {
:       char[] line = lines[0];
:       lines = lines[1..$];
:       done = ! lines.length;
:    together B:
:    }
:
:    together C:
: }

: void interact_print()
: {
:    together A:
:
:    while(1) {
:      together B:
:        if (other done)
:            break;
:
:        writefln("%s", other line);
:    }
:
:    together C:
: }

: int sort_and_unique()
: {
:    together A:
:    
:    int i = 0;
:    bool[char[]] uniquify;
:
:    while(1) {
:      together B:
:        if (other done)
:          break;
:        
:        if (! uniquify.find(other line)) {
:            writefln("%s", other line);
:            uniquify[other line] = true;
:        }
:    }
:
:    int count;
:
:    together C:
:      foreach(char[] line; uniquify.keys) {
:        writefln("%s", line);
:        count++;
:      }
:
:    return count;
: }

Some sort of controlling function would be needed to "zip" together
the facet functions, providing data for their constructors.

: void readAndUnique()
: {
:
:   int count1;
:
:   bind_facets {
:     // runs the following facets simultaneously
:     // nothing can be here except calls to facets
:
:     read_from_file("a.txt");
:     count1 = sort_and_unique();
:   }
:
:   writefln("%d unique lines", count1);
: }

The primary goal here is to allow users to "zip in" locking
(i.e. semaphores), database access, widget set calls, etc.

One other feature: for maximum control, the "together" statement
should take an optional number:

together(140) C:

The facets with the lowest numbers would be executed first after the
number is hit.  The facets would all run in the same thread, but the
compiler is free to mix, reorder, etc. code from different facets as
long as they all stop and wait for each other to get to the same
"together" label.

Facet "binding", i.e. construction of the functions would probably
have to happen at compile time like a template or mixin instantiation.

Kevin
Feb 28 2006
next sibling parent reply Ben Phillips <Ben_member pathlink.com> writes:
This idea is interesting, but also confusing. From what I can tell using
"together" means that the facets 
don't execute linearly, since they depend on each other. Imho this would be very
confusing to code and 
even more confusing to understand. I even have trouble understanding your
example, I can get the 
basics but its confusing how the scopes of all the facets seem to be linked
through the use of "other". 
This leads me to believe that facets would also be very difficult to implement
in a compiler.

In article <du3fib$2blj$1 digitaldaemon.com>, Kevin Bealer says...
I like the on_scope_exit() and friends, by the way.

But it got me thinking about an idea I had.  It's a bit like aspect
oriented programming, but that is usually done with classes.  This is
a function based one.  The name I'm thinking of for this is "facet
programming", sort of suggesting "function based aspects".

The idea is to have semi-independent functions that can be "zipped"
together at specified points to combine different implementations of
sub-functionalities, or to add and remove those elements.

Imagine that I have one function that reads lines of data from a file,
another that reads from a user interface, and I want to process the
data one line at a time.  I can also have several "consumers" of the
data.  Here I show two of each.

However, the real power would be in writing a conceptual "script" for
some sequence of interactions (this is represented as "A: B: C:"
below).  This script would allow dozens or hundreds of classes to
interact at controlled places.  Currently this requires writing a
master function that knows everything about each of the interactions
between the parts, for all the parts.

The word "facet" defines a facet of the program; "together" indicates
points that all functions need to reach before any can proceed.  The
modifier "other" means "find this variable name in one of the other
aspects instead of locally".  This is sort of like "extern" in C.

: facet void read_from_file(char[] fname)
: {
:    together A:
:       LineReader lr = new LineReader(name);
:    
:    bool done = lr.eof();
:
:    while(! done) {
:       char[] line = lr.readLine();
:       done = lr.eof();
:    together B:
:    }
:   
:    together C:
:       lr.close();
: }

: facet void parse_lines(char[][] lines)
: {
:    together A:
:
:    bool done = ! lines.length;
:
:    while(! done) {
:       char[] line = lines[0];
:       lines = lines[1..$];
:       done = ! lines.length;
:    together B:
:    }
:
:    together C:
: }

: void interact_print()
: {
:    together A:
:
:    while(1) {
:      together B:
:        if (other done)
:            break;
:
:        writefln("%s", other line);
:    }
:
:    together C:
: }

: int sort_and_unique()
: {
:    together A:
:    
:    int i = 0;
:    bool[char[]] uniquify;
:
:    while(1) {
:      together B:
:        if (other done)
:          break;
:        
:        if (! uniquify.find(other line)) {
:            writefln("%s", other line);
:            uniquify[other line] = true;
:        }
:    }
:
:    int count;
:
:    together C:
:      foreach(char[] line; uniquify.keys) {
:        writefln("%s", line);
:        count++;
:      }
:
:    return count;
: }

Some sort of controlling function would be needed to "zip" together
the facet functions, providing data for their constructors.

: void readAndUnique()
: {
:
:   int count1;
:
:   bind_facets {
:     // runs the following facets simultaneously
:     // nothing can be here except calls to facets
:
:     read_from_file("a.txt");
:     count1 = sort_and_unique();
:   }
:
:   writefln("%d unique lines", count1);
: }

The primary goal here is to allow users to "zip in" locking
(i.e. semaphores), database access, widget set calls, etc.

One other feature: for maximum control, the "together" statement
should take an optional number:

together(140) C:

The facets with the lowest numbers would be executed first after the
number is hit.  The facets would all run in the same thread, but the
compiler is free to mix, reorder, etc. code from different facets as
long as they all stop and wait for each other to get to the same
"together" label.

Facet "binding", i.e. construction of the functions would probably
have to happen at compile time like a template or mixin instantiation.

Kevin
Mar 01 2006
parent Kevin Bealer <Kevin_member pathlink.com> writes:
In article <du4f5r$t0d$1 digitaldaemon.com>, Ben Phillips says...
This idea is interesting, but also confusing. From what I can tell using
"together" means that the facets 
don't execute linearly, since they depend on each other. Imho this would be very
confusing to code and 
even more confusing to understand. I even have trouble understanding your
example, I can get the 
basics but its confusing how the scopes of all the facets seem to be linked
through the use of "other". 
This leads me to believe that facets would also be very difficult to implement
in a compiler.
Here is what I'm thinking on how to implement it, using a simpler example: : facet void compute_n() : { : int n; : together C: : writefln("%d", n); : } : facet void write_x() : { : writefln("x"); : together A: : n += 3; : writefln("x"); : together B: : n += 5; : writefln("x"); : together C: : } : facet void write_y() : { : writefln("y"); : together A: : n ++; : writefln("y"); : together B: : n += 2; : writefln("y"); : together C: : } : void write_x_and_y() : { : bind_facets { : write_x(); : write_y(); : compute_n(); : } : } The compiler would read all this in, and produce a function like this: : void write_x_and_y() : { : int n; : writefln("x"); : writefln("y"); : : //together A: : n += 3; : writefln("x"); : n ++; : writefln("y"); : : //together B: : n += 5; : writefln("x"); : n += 2; : writefln("y"); : : //together C: : writefln("%d", n); : } The "together" and "other" directives are compiler hints - at runtime there is just the one resulting function. Kinda like a mixin. The scopes of the facets are effectively seperate, with "other" acting a little like C language 'extern' - it tells the compiler that its okay to look in other facets for this variable name. It is an error to use "other" with a name that appears in more than one facet (i.e. it must be unambiguous). Kevin
Mar 01 2006
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Kevin Bealer wrote:
 I like the on_scope_exit() and friends, by the way.
 
 But it got me thinking about an idea I had.  It's a bit like aspect
 oriented programming, but that is usually done with classes.  This is
 a function based one.  The name I'm thinking of for this is "facet
 programming", sort of suggesting "function based aspects".
 
 The idea is to have semi-independent functions that can be "zipped"
 together at specified points to combine different implementations of
 sub-functionalities, or to add and remove those elements.
I really like the potential of AOP in general, however this seems more of a proposal to add language support for concurrent programming. And while I think that idea has merit as well, I'm not sure I want to confuse the two. In my experience, AOP is a way to inject functionality into arbitrary code blocks without the need to do so manually. This can be invaluable during maintenance if a need arises that the original code wasn't designed for, as it doesn't require the original code to have made any attempt at allowing for such code injection. But parallelism is a separate issue as far as I'm concerned. Sean
Mar 01 2006
parent Kevin Bealer <Kevin_member pathlink.com> writes:
In article <du4njo$19sf$1 digitaldaemon.com>, Sean Kelly says...
Kevin Bealer wrote:
 I like the on_scope_exit() and friends, by the way.
 
 But it got me thinking about an idea I had.  It's a bit like aspect
 oriented programming, but that is usually done with classes.  This is
 a function based one.  The name I'm thinking of for this is "facet
 programming", sort of suggesting "function based aspects".
 
 The idea is to have semi-independent functions that can be "zipped"
 together at specified points to combine different implementations of
 sub-functionalities, or to add and remove those elements.
I really like the potential of AOP in general, however this seems more of a proposal to add language support for concurrent programming. And while I think that idea has merit as well, I'm not sure I want to confuse the two. In my experience, AOP is a way to inject functionality into arbitrary code blocks without the need to do so manually. This can be invaluable during maintenance if a need arises that the original code wasn't designed for, as it doesn't require the original code to have made any attempt at allowing for such code injection. But parallelism is a separate issue as far as I'm concerned. Sean
I agree - AOP is good and parallelism is not as interesting. The goal of my proposal is not to parallelize code, though - in fact I don't really see how it can help in that regard. I think there are significant benefits to be had from splitting functions into smaller functions - each smaller function dealing with a specific aspect of the task at hand. However, I'm not as concerned about AOP as a way of maintaining code that did not intend to have a given feature - I want to design code to be 'horizontally composable' from the start. If I know that a transaction has a script like this (to make up an example): a. Set up GUI and connect to local DB and remote server. b. Get request from user. c. Find local data in db. d. Send it to a remote service. e. Poll until done. f. Read and parse the output. g. Display the output on GUI, and maybe in a local file. This design will result in a large, complex function. You can break that up into sub functions, which is a good idea, but... we would like all the DB stuff to be grouped, and all the GUI stuff to be grouped, and all the user interaction to be grouped. This grouping is hard to do if the actions are interspersed in this way. What you end up with is a dozen little functions -- each one potentially interacts with database, gui, and remote server. The code that deals with the GUI, for instance, is probably spread over all these functions. The facet idea, allows this script to be an explicit part of the design. It allows the GUI code to be grouped in one location. Likewise for the DB code, remote connection (and synchronization, and ..). Each can be written as seperate function-like elements that are later joined. If you change the DB locking, you only need to touch the DB facet. What object oriented does for data is seperate things that are unconnected, and group things that are related. I want to do this for code - split things up that are unrelated, and group things that are related. Just as the scope_exit() macro groups construction and cleanup of a resource. Right now, proper design can make this design cleaner, but facets (or maybe a similar, better mechanism) could make it MUCH cleaner. Kevin
Mar 01 2006