Hi all,

I think it would be a bit presumptuous to call this "FIG's equations of
software", but I stumbled across an interesting phenomenon while
developing my Ocean compiler system: the idea of derivatives and
integrals of software programs.

For examples of the concepts, I'll be presenting them in a simple
object-capability language, called Sandstone.  Here is a sample source
file:
----------------------------------------------------------------------
.new(Out, Name): {
  Hello = load('hello.ss').new('World');
  return {
   .greeting(Greeting: string): {
      Out.format('~s, ~s and ~s!~n', [Greeting, Name, Hello.msg]);
   };
   .count(Name: atom): {
      return 123;
   }
  }
};
.main(): {
  Hello = .new(stdout, 'Michael');
  Hello.greeting('Hello');
}
----------------------------------------------------------------------

And a grammar fragment (I have not implemented this, so it may contain
bugs, but I'm sure that somebody better with OMeta would be able to hack
it up quite quickly):

Main        = Decl (_ ';' _ Decl)* _ ';'? _ !.
Decl        = Ident Prototype? ':' _ Expr
Ident       = '.' [_A-Za-z] [_A-Za-z]*
Prototype   = '(' _ Formal (_ ',' _ Formal)* _ ')'
Formal      = Var | Var ':' _ Atom
Var         = [A-Z_] [A-Za-z_0-9]*
Expr        = Call | Block | List | Tuple | Number | String ...
Block       = '{' _ Statement (_ ';' _ Statement)* _ ';'? _ '}'
Statement   = Decl | Call (_ Call)*
Call        = Symbol Tuple? (Ident Tuple)*
Symbol      = [^(). \t\r\nA-Z] [^(). \t\r\n]+
List        = '[' _ Expr (_ ',' _ Expr)* _ ']'
Tuple       = '(' Expr (_ ',' _ Expr)* _ ')'

The semantics may seem obvious from the example, but they are actually
designed to be radically variable.  The syntax is intended to be a
human-friendly ocap assembly language of sorts.  Just picture this
syntax with the "kernel" idea described in Ian's paper on the VPRI site
(which I had a little hand in implementing)... the semantics are
determined by the specific kernel (which may be dynamically extended or
swapped) much more than the "actual" code.

Definitions
***********

I would like to provide a more mathematical explanation of the following
concepts, but I'm afraid my limited (and ancient) calculus knowledge is
not up to the challenge.  I rely on hand-waviness and hopefully you all
will still get my drift.

A method has an easily computable derivative with respect to a
parameter... it is done by dropping the parameter from the argument
list, and transforming all of its method calls into implicit kernel
calls:

d.new(Out, Name)
----------------
     dOut
           = .new(Name): {
               Hello = load('hello.ss').new('World');
               return {
                  .greeting(Greeting: string): {
                     format('~s, ~s and ~s!~n', [Greeting, Name, Hello.msg]);
                  };
                 .count(Name: atom): {
                    return 123;
                 }
              }
            };

The integral is a little more interesting.  We perform an integral with
respect to a method and a set of implicit kernel calls.  The process
introduces a fresh name as the first parameter, and makes all the
implicit calls in the set into method calls on the first param.

integral(.new(), {format, return})
          = .new(K, Name): {
               Hello = load('hello.ss').new('World');
               K.return({
                 .greeting(Greeting: string): {
                    K.format('~s, ~s and ~s!~n', [Greeting, Name, Hello.msg]);
                 };
                 .count(Name: atom): {
                    K.return(123);
                 }
              })
            };

Unlike derivation, integration requires an oracle who understands the
semantics of the function.

This gets interesting when we integrate over .new(Name)'s implicit eval()
function (provided the oracle handles it):

integral(.new(Name), {format, eval})
          = .new(K, Name): {
              return K.eval({'Name': Name, 'K': K},
                [
                  ['Hello', '=', ['.new', ['load', (utf8, 'hello.ss')], 
'World']],
                  ['return',
                    (block, [('.greeting', [('Greeting', 'string')], 
                     [['.format', K, (utf8, '~s, ~s and ~s!~n'),
                                     (list, ['Greeting', 'Name',
                                               (slot, 'Hello', 'msg')])]]),
                     ('.count', [('Name', 'atom')], [['return', 123]])])
                  ]
               ])
            };

Laws
****

1. Abstraction:

  .method2(K, ...) = integral(.method(...), {prim1, ..., primN})

.method2 has had its implicit primitives {prim1, ..., primN} reified as
explicit calls to K.

2. Evaluation/interpretation.

  .meta(K, typeof(A1), ..., typeof(An)) === integral(.method(A1, ..., An), 
{eval})
  .meta(I, ...) = value of .method interpreted using kernel I

3. Optimization/compilation:

  .method3(...) = d.method(X, ...)
                  ---------------
                       dX

.method3 no longer varies with X, but instead relies on its evaluator to
provide the services previously attributed to X.

4. Entropy.

 d integral(.method, prims)
----------------------------- = .method
d(first argument of integral)

but

integral(d.method(A, ...), prims)
         ---------------
               dA

is not necessarily equivalent to .method since collapsing A's calls into
the environment may result in information loss, and the correct prims
set must be identified by an oracle as well.


Comments?
*********

I want to develop these ideas further, but I imagine it'll be quite some
time before I have something worthy of a paper or even a memo, so I
want to solicit public discussion even though they're in their infancy.

-- 
Michael FIG <mich...@fig.org> //\
   http://michael.fig.org/    \//

_______________________________________________
fonc mailing list
fonc@vpri.org
http://vpri.org/mailman/listinfo/fonc

Reply via email to