Subject: P: single assignment semantics

From: Alex Rousskov <[email-masked]>
Date: Mon, 27 Oct 2003 14:41:45 -0700 (MST)

I'd like to discuss a problem with assignment semantics in P. I am
curious what specific solutions other languages in similar domains,
including IRML, provide (if any).

IIRC, Geetha Manjunath spotted an important issue with P: Current
draft says that assignment is, essentially a macro. The value of a
name is a value of an expression and the time of use, not at the time
of assignment:

	name := foo(bar);
	if (name) {
		// A
	}

	...

	if (not name) {
		// B
	}

Using the macro semantics, both A and B code pieces may be executed if
foo(bar) changes its value between the two if-statements. This macro
semantics allows for simple and/or efficient implementation. The
interpreter (in a broad sense) does not have to keep the value of
foo(bar) around between the if-statements. If that value is large
(e.g., a response body) such optimizations could be crucial.

On the other hand, macros are confusing for programmers, especially
because it is often difficult to know whether the named expression can
be changed. Assume, for example, that name is a response
body-dependent expression and code A above executes a callout service
on the response headers. By the time the service call completes, the
processor may read a few more chunks of that response body and the
value of the named expression can change. A simpler example would be:

	current_time := Time.now();     // interpreted in the morning
	...
	if (current_time.isEvening()) { // can this become true?
		...
	}

A different option (let's call it "immediate constant assignment")
exists: We can require the value of the expression at the time of the
assignment to be stored under the provided name and never change. This
means that some unused expressions would be computed and [very] large
values would have to be stored.

A yet another option (let's call it "lazy constant assignment")
exists: We can require the value of the expression at the time of the
first use (rather than first assignment) to be stored under the
provided name and never change after that. This option has an
advantage over immediate constant assignment in that unused
expressions are not evaluated, but it still has to store [very] large
values if an expression is used more than once.

While IRML does not have assignments, it does evaluate expressions. I
wonder if there is an implicit or explicit guarantee that the same
property is evaluated to the same value before and after a delay (such
as service execution). Does IRML assume that all properties are known
and "constant" just before the script is interpreted?

Thanks,

Alex.

---

From: Andre Beck <[email-masked]>
Date: Tue, 28 Oct 2003 10:43:14 -0500

> and "constant" just before the script is interpreted?

In the current IRML draft we require that IRML interpreters re-evaluate 
rule conditions and the referenced message property values after each 
service execution. As you pointed out, message properties and in 
particular message header values can change as a result of service 
executions. A content adaptation service, for example, may modify the 
content type and subsequent service operations may not make sense any 
more once the content type of a message has changed. So I don't believe 
we can assume that all properties are constant while we evaluate a set 
of rules for a given message. On the other hand, such a requirement 
would rule out certain performance optimizations. When we discussed this 
issue a while ago, somebody proposed to divide OPES services into those 
that may modify message header values (e.g. a content adaptation 
service) and those that won't (e.g. a logging service) and optimize the 
rule processing based on this knowledge.

-Andre

---

From: Alex Rousskov <[email-masked]>
Date: Tue, 28 Oct 2003 10:00:13 -0700 (MST)

On Tue, 28 Oct 2003, Andre Beck wrote:

> service execution.

Re-evaluation is essentially a loop, right? Does this re-evaluation
open a possibility for an infinite loop? How can we be sure that both
of the cases below are covered:

	- a service that needs to be applied more than
	  once (e.g., after each header X change) is applied
	  as many times as necessary

	- a [misconfigured] set of rules does not lead
	  to infinite loops

Should we simply rely on timeouts and other external measures to break
loops? Or should we try to prevent loops on a language level? Or am I
seeing loops when none exist?

Thanks,

Alex.

---

From: Andre Beck <[email-masked]>
Date: Tue, 28 Oct 2003 14:12:24 -0500

> seeing loops when none exist?

I think the latter is the case. ;-) At least IRML does not allow such 
loops. When I talked about the re-evaluation of rule conditions I did 
not mean that a single rule can or should be evaluated more than once 
for a given message. Rather what I meant was that if the same message 
property is referenced in two different rules, then the rule processor 
must not assume that the property value will be the same for both rules.

I don't think it's a good idea to get into the business of loops and 
loop prevention by allowing the rule processor to evaluate a rule a 
second time if a message propery value changes. In fact, I think such 
behavior would potentially lead to very unintended results because the 
order in which rules (and services for that matter) are processed can 
make a big difference. The order in which rules are processed should be 
determined before they are evaluatated.

-Andre

---

From: Alex Rousskov <[email-masked]>
Date: Tue, 28 Oct 2003 13:38:50 -0700 (MST)

On Tue, 28 Oct 2003, Andre Beck wrote:

> I think the latter is the case. ;-) At least IRML does not allow
> such loops. When I talked about the re-evaluation of rule conditions
> I did not mean that a single rule can or should be evaluated more
> than once for a given message. Rather what I meant was that if the
> same message property is referenced in two different rules, then the
> rule processor must not assume that the property value will be the
> same for both rules.

Ah, I see. In P, since it has variables, the same caveat becomes even
more confusing for rule writers, I think. In IRML, the rule writer
should assume that every external property may change between service
invocations (at least). In P, the rule writer may have to assume that
even local variables change between service invocations (at least).

It would be nice to either avoid variable changes or at least make
them less transparent. In your opinion, what is more important:

    - optimizing performance when storing potentially
      large expression values in a variable

    - optimizing programmer understanding when using
      variables (i.e., making the interface more natural
      so that programmer's expectations match reality)

I wonder if it make sense to offload the above decision to programmer?
We could give rule writers explicit control over macro-versus-constant
choice by introducing references (parameter-less macros) in addition
to constant variables.  For example,

    /* no performance penalty, natural/expected results */
    constant name := small_value;

    /* no performance penalty, possibly confusing results */
    reference name := huge_value;

    /* no performance penalty, possibly confusing results */
    reference name := small_value;

    /* performance penalty, natural/expected results */
    constant name := huge_value;

What do you think? Is it worth the trouble supporting the above
interface? Or should we make this decision for the programmer, in P
Core?

Or would it be possible to delegate the decision to module writers,
like I proposed for string case sensitivity support? Each object would
be either copied or referenced, depending on objects type, as defined
by object creator (a module):

    /* no performance penalty, natural/expected results */
    name := small_object.cheap_operation(small_value)

    /* no performance penalty, possibly confusing results */
    /* copy small_* and cheap_operation, */
    /* reference huge_object and expensive_operation */
    name := huge_object.cheap_operation(small_value);
    name := huge_object.expensive_operation(small_value);
    name := huge_object.expensive_operation(huge_value);
    name := small_object.expensive_operation(small_value);
    name := small_object.expensive_operation(huge_value);
    ...

The latter approach puts the optimization burden on module writers,
which is good. Should we combine the two:

    /* rely on expression's module(s) to copy or reference */
    name := expression;

    /* force copying: performance penalty but expected results */
    constant name := expression;

Comments? Better ideas?

> second time if a message propery value changes. In fact, I think
> such behavior would potentially lead to very unintended results
> because the order in which rules (and services for that matter) are
> processed can make a big difference. The order in which rules are
> processed should be determined before they are evaluatated.

I agree.

Alex.

---

From: Geetha Manjunath <[email-masked]>
Date: Wed, 29 Oct 2003 09:58:14 +0530

How about having a new reserved member for every variable? Access to
this member triggers an evaluation of the expression and returns the
result.

name := expression;
const := name.value; 

Now the programmer has the benefit of both the alternatives. Ofcourse,
the compiler/runtime can also optimize and avoid any reevaluation
wherever possible.

regards
Geetha

---

From: Geetha Manjunath <[email-masked]>
Date: Wed, 29 Oct 2003 10:19:41 +0530

Andre Beck wrote:
> more once the content type of a message has changed. 

Under this condition, I think, it may be cumbersome to actually nest the
rest of the code under another IF.. 

How does P handle this? 
You may want to introduce a new statement in 'P' that just 'ends' the
rule processing and returns.. Alternatively you may want to specify a
method in core to do so , core.end(), core.exit() or something like
that.

regards
Geetha

---

From: Geetha Manjunath <[email-masked]>
Date: Wed, 29 Oct 2003 10:29:12 +0530

Alex,

While I do strongly agree about the possibility of different values of
the same expression at different points in a P program (due to
adaptation and so on), this example of yours confused me...
> Assume, for example, that name is a response
How can the processor read a few more chunks while performing the rule
processing ? If this is so, then I suppose, we would end up in a non
deterministic behaviour!

In other words, my question is probably about the vectoring point.. what
are options for vectoring point for a non-HTTP protocol, for example?

Sorry - if this is already specified in a different draft, pl give me a
pointer to that...

regards
geetha

---

From: Alex Rousskov <[email-masked]>
Date: Wed, 29 Oct 2003 08:51:12 -0700 (MST)

On Wed, 29 Oct 2003, Geetha Manjunath wrote:

> Andre Beck wrote:
>
> > In the current IRML draft we require that IRML interpreters
> > re-evaluate rule conditions and the referenced message property
> > values after each service execution. As you pointed out, message
> > properties and in particular message header values can change as a
> > result of service executions. A content adaptation service, for
> > example, may modify the content type and subsequent service
> > operations may not make sense any more once the content type of a
> > message has changed.
>
> Under this condition, I think, it may be cumbersome to actually nest
> the rest of the code under another IF..
>
> How does P handle this?

It is up to the programmer or IDE to handle any inter-rule
dependencies. Semantic dependencies among rules are to complex for P
to check/enforce them, IMO.

> method in core to do so , core.end(), core.exit() or something like

Yes, that's already on the todo list. Section 5 of the draft says:
XXX: add Core.interpreter.stop and Core.interpreter.restart methods.

Alex.

---

From: Alex Rousskov <[email-masked]>
Date: Wed, 29 Oct 2003 09:06:21 -0700 (MST)

On Wed, 29 Oct 2003, Geetha Manjunath wrote:

> > Assume, for example, that name is a response body-dependent
> > expression and code A above executes a callout service on the
> > response headers. By the time the service call completes, the
> > value of the named expression can change.
>
> How can the processor read a few more chunks while performing the
> rule processing ?

The P interpreter instance serving this particular transaction will
block, but the proxy executing that interpreter instance does not have
to block.  All production proxies I know do not block on external
operations such as a callout service call. They continue to work on
other items. These "other items" usually include network I/O for
pending transactions, possibly including the ones for which the rules
are being interpreted.

Thus, depending on the design, a proxy may continue to read response
body while the service is doing its thing and the interpreter instance
is waiting.

In fact, in most cases, a complete body will _not_ be available at any
single time -- it is just too big to cache (the service can cache the
body if it needs to, but the proxy may not).

> If this is so, then I suppose, we would end up in a non
> deterministic behaviour!

Depends on what you mean by "non-determinism" in this case, I guess.

You agree above that adaptations can cause the same expression to have
different values. "Reading a few more chunks" is just another
adaptation, if you will. There is no difference from P point of view.

We can (and probably should) demand that interpreters do not block
except for a few known points like service executions. While writing
ruleset with adaptation rules is close to programming with threads, we
want to avoid parallel processing surprises as much as possible. P
"threads" can (and probably should) have well-documented preemption
points.

> In other words, my question is probably about the vectoring point..
> what are options for vectoring point for a non-HTTP protocol, for
> example?

I do not think the above is related to vectoring points. The problem
exists even if there is a single vectoring point. Multiple vectoring
points just introduce more opportunities for something to be changed
while the rules are interpreted.

Did my response clarified the problem enough?

Thanks,

Alex.

---

From: Andre Beck <[email-masked]>
Date: Wed, 29 Oct 2003 15:30:43 -0500

> body if it needs to, but the proxy may not).

I agree and that's in fact why I think P programs should not be allowed 
to reference message bodies. In my opinion, P programs should only serve 
as high-level filters that invoke OPES services based on message header 
values (e.g. HTTP content-type) and maybe system and environment 
variables (e.g. system time). If a P program triggers the execution of a 
service, then the service logic within that service can evaluate all 
message properties including the message body to determine if there is a 
need to process the message. I don't think it's a good idea to move that 
kind of service logic into P programs. By restricting P programs in this 
way we would also render the copy vs. reference question essentially a 
non-issue simply because we can assume that message header values are 
always small and copy operations would not have a significant 
performance impact. I would therefore favor the 'programmer-friendly' 
constant approach over the macro approach or any combination thereof.

-Andre

---

From: Alex Rousskov <[email-masked]>
Date: Wed, 29 Oct 2003 14:37:37 -0700 (MST)

On Wed, 29 Oct 2003, Andre Beck wrote:

> I agree and that's in fact why I think P programs should not be
> allowed to reference message bodies. In my opinion, P programs
> should only serve as high-level filters that invoke OPES services
> based on message header values (e.g. HTTP content-type) and maybe
> system and environment variables (e.g. system time). If a P program
> triggers the execution of a service, then the service logic within
> that service can evaluate all message properties including the
> message body to determine if there is a need to process the message.
> I don't think it's a good idea to move that kind of service logic
> into P programs.

I agree that P primary objective is to pre-select good candidates for
adaptation (service application). It is up to the service to make the
final call and perform adaptations or get out of the loop.

There is a fine line here, of course. In may cases, selecting a good
candidate would require looking at the body. For example, a P module
that guesses content type would do that. Without such a module, one
would have to pass virtually everything to the content-type-specific
service because Content-Type headers are often missing or wrong. Note
that the said P module would only need to look at a few first bytes of
the content.

> By restricting P programs in this way we would also render the copy
> vs. reference question essentially a non-issue simply because we can
> assume that message header values are always small and copy
> operations would not have a significant performance impact. I would
> therefore favor the 'programmer-friendly' constant approach over the
> macro approach or any combination thereof.

True.

I am not ready to cast my vote here. I think we need to consider more
use cases. How, for example, do you propose to handle a case where a
callout service wants to adapt all GIF images. and only them? We can
assume HTTP application protocol for now.

Also, it seems that your suggestion is based on an assumption that
bodies are large and headers are small. This is usually true for HTTP,
but may not be true for other application protocols. If we go down
this path, how would you define X in "P programs should not be allowed
to reference X"? Would it be application binding specific?

Finally, is size of the data the only concern here?

Thanks,

Alex.

---

From: Andre Beck <[email-masked]>
Date: Wed, 29 Oct 2003 18:02:32 -0500

> assume HTTP application protocol for now.

One approach would be to write a P program that forwards only those HTTP 
responses to the service that do not have a content type of "text/*". 
This should significantly reduce the number of unnecessary service 
invocations. But I agree that in this example it may make sense to give 
a P program access to the first few bytes of the message body. This 
would still be a small object, though. Do you have an example that would 
require the handling of large objects?

> to reference X"? Would it be application binding specific?

Generally speaking, I would say that P programs should be restricted to 
the evaluation of signaling/control and meta data which is all typically 
contained in message headers. I also think that it's safe to assume that 
signaling/control/meta data is typically smaller than the data it is 
assciated with. In practice, I think we should look at those message 
properties that OPES processors need to inspect for their normal 
operation anyway. For example, do Web caches typically look at HTTP 
bodies in order to decide whether or not to cache an object? If not, 
then maybe this would be a message property that should rather be 
inspected by an OPES service application running on a dedicated and 
specialized callout server. I think whenever the evaluation of a 
property is likely to be expensive and could therefore interfere with 
the normal operation of an OPES processor, then we should disallow this 
operation in P.

-Andre

---

From: Alex Rousskov <[email-masked]>
Date: Thu, 30 Oct 2003 09:33:40 -0700 (MST)

On Wed, 29 Oct 2003, Andre Beck wrote:

> > I am not ready to cast my vote here. I think we need to consider
> > more use cases. How, for example, do you propose to handle a case
> > where a callout service wants to adapt all GIF images. and only
> > them? We can assume HTTP application protocol for now.
>
> One approach would be to write a P program that forwards only those
> HTTP responses to the service that do not have a content type of
> "text/*".  This should significantly reduce the number of
> unnecessary service invocations.

Text/* files are not that common, relatively speaking. The actual
reduction with the above technique would be less than 20% (by count)
and less than 10% (by volume). Here is a daily stats snapshot taken at
our IRCache proxies (www.ircache.net), for example:

   Top 20 Object Types:

   3173614 61% Image           9501602 kbytes 19%     3065 bytes/obj
   1035836 20% Other          22461506 kbytes 45%    22204 bytes/obj
    413219  8% Query           2264820 kbytes  5%     5612 bytes/obj
    185256  4% HTML            1666142 kbytes  3%     9209 bytes/obj
    184296  4% Directory       1993028 kbytes  4%    11073 bytes/obj
    115932  2% Lookup          1036971 kbytes  2%     9159 bytes/obj
     26990  1% Executable      5469681 kbytes 11%   207519 bytes/obj
     14140  0% Bundle          1735770 kbytes  3%   125702 bytes/obj
     10733  0% Text              68388 kbytes  0%     6524 bytes/obj
     10548  0% SHTML            152145 kbytes  0%    14770 bytes/obj
     10315  0% PDF             1355587 kbytes  3%   134573 bytes/obj
      6374  0% Audio            193083 kbytes  0%    31019 bytes/obj
      5746  0% Movie           1897433 kbytes  4%   338143 bytes/obj
      4407  0% Applet            21822 kbytes  0%     5070 bytes/obj
       981  0% Software          15537 kbytes  0%    16218 bytes/obj
        59  0% PostScript         9698 kbytes  0%   168324 bytes/obj
        14  0% ISMAP                15 kbytes  0%     1108 bytes/obj
         6  0% Drawing             518 kbytes  0%    88512 bytes/obj

But we may be getting too specific here... One could, of course, come
up with a set of nested rules that guess most images correctly based
on just HTTP headers. My concern is that if we simply prohibit
body-related functionality, there will be "too many" corner cases
where that functionality is essential.

For example, should P be able to block viruses that spread via simple
10KB HTTP POST requests? If yes, we give network operators a powerful
tool. If not, we tell network operators to rely on 3rd party modules
being available "soon enough" for each new virus.

> But I agree that in this example it may make sense to give a P
> program access to the first few bytes of the message body. This
> would still be a small object, though. Do you have an example that
> would require the handling of large objects?

I do not have a good one. If an example handles something very large,
you would say that a service should be used instead of a module.
For example, why do we think it is appropriate to prohibit support
for this kind of module/function in P:

	if (VirusVendorModule.findVirus(message.body)) {
		...
	}

I think it is possible to support the above in P, but I am not sure it
is a good idea to do so. More precisely, I do not know where to draw
the line:

	VirusVendorModule.findVirus(message.body)
	VirusVendorModule.virusProbability(message.bodyprefix) > 30%
	ImageVendorModule.isGif(message.body)
	ImageVendorModule.gifProbability(message.bodyprefix) > 30%
	...

> Generally speaking, I would say that P programs should be restricted
> to the evaluation of signaling/control and meta data which is all
> typically contained in message headers. I also think that it's safe
> to assume that signaling/control/meta data is typically smaller than
> the data it is assciated with.

And yet you did agree that allowing P modules to peek at small parts
of the body might be useful for things like content type determination
or, perhaps, blocking viruses in POST queries.

I do share your concerns about the overheads and complexities of body
handling in P. I am trying to find a flexible scheme that will cover
most cases with ease and leave some freedom for accommodating some
corner cases with effort.

One answer could be that something like findVirus(message.body) or
isGif(message.body) MUST be implemented as a service but may have a
function-call interface to P. We already have to deal with service
execution (something that is not well documented in P or IRML yet) so
we would not be adding much more complexity; we would just need to
document an interface where a service can return a value for P
interpreter to use.

Instead of writing a bunch of sequential calls to services that would
ignore most of the content:

	apply(service1);
	apply(service2);
	apply(service3);

the programmer would be able to optimize:

	if (apply(service1) returns FooBar) {
		apply(service2);
	} else {
		apply(service3);
	}

The latter is then no different (on a conceptual level) from

	if (service1() == FooBar) {
		service2();
	} else {
		service3();

	}

Do you see what I am getting at?

> In practice, I think we should look at those message properties that
> OPES processors need to inspect for their normal operation anyway.
> For example, do Web caches typically look at HTTP bodies in order to
> decide whether or not to cache an object? If not, then maybe this
> would be a message property that should rather be inspected by an
> OPES service application running on a dedicated and specialized
> callout server.

"Straight" web caches look at bodies only if they need to handle
transfer encodings and such. However, there are some HTTP proxies
that adapt bodies. We can say that those proxies are out of scope;
they should be using services instead.

> I think whenever the evaluation of a property is likely to be
> expensive and could therefore interfere with the normal operation of
> an OPES processor, then we should disallow this operation in P.

If we try to do something like that, we are still faced with the same
specification problem: How do you define "likely to be expensive"? Is
it expensive for 10% of use cases? 90%?

Is looking at the first 100 bytes of a body expensive? Is looking at
all already available bytes of a body expensive? What if the entire
10MB body is available because it is cached or pre-positioned on the
proxy?

Alex.

---

From: Andre Beck <[email-masked]>
Date: Thu, 30 Oct 2003 17:21:25 -0500

> our IRCache proxies (www.ircache.net), for example:

Interesting.

> being available "soon enough" for each new virus.

I am not sure I understand this scenario. Are you suggesting that there 
should be P module that provides a function to do the virus scanning?

> document an interface where a service can return a value for P

Yes, I like that idea a lot. Conceptually, there is really not much of a 
difference between a P module and an OPES service. But since we are 
trying to define a framework for OPES services I think it makes a lot of 
sense to provide functions such as isGIF or findVirus as OPES services. 
Plus, this would also allow an OPES domain administrator to make a local 
decision as to whether a service like isGIF should run locally on the 
OPES processor or on a remote callout server. This should be transparent 
to P programs, of course.

I think if we decide to go down this route, then it would be a natural 
choice to restrict P module access to message headers while OPES 
services can access the entire message. Of course, a utility service 
like isGIF would only need to see the first few bytes of the message 
body and would therefore be less expensive to invoke than a service that 
needs to operate on larger parts of the message body. This could be one 
criteria that determines whether or not a service is a good candidate 
for local execution.

-Andre

---

From: Alex Rousskov <[email-masked]>
Date: Thu, 30 Oct 2003 17:03:35 -0700 (MST)

On Thu, 30 Oct 2003, Andre Beck wrote:

> > For example, should P be able to block viruses that spread via
> > simple 10KB HTTP POST requests? If yes, we give network operators
> > a powerful tool. If not, we tell network operators to rely on 3rd
> > party modules being available "soon enough" for each new virus.
>
> I am not sure I understand this scenario. Are you suggesting that
> there should be P module that provides a function to do the virus
> scanning?

Not exactly, sorry for being so cryptic.

I try to think in more general terms while having specific
applications in mind. In this particular case, the application is,
essentially, a virus scanner that supports a single virus that spreads
via POST requests. The general mechanism in P would be something like
a string search within a message body and response generation
(something many proxies already support in one way or another):

	if (request.body contains "virus_signature") {
		generateResponse("request_blocked.html");
		Core.interpreter.stop();
	}

We already know about killer viruses that spread via GET requests. Our
web server logs still get a lot of those requests, years after the
viruses were released. The above interface can filter out GET-based
viruses too, of course. I am sure POST-based viruses either already
exist or will exist. They are more difficult to deal with because
routers and even L7 switches have trouble looking at POST bodies.

Here we concentrate on the ability of a network administrator to block
something like that immediately after reading about it on slashdot,
not via virus scanner upgrades (which is the correct long-term way of
doing it).

> > isGif(message.body) MUST be implemented as a service but may have
> > a function-call interface to P. We already have to deal with
> > service execution (something that is not well documented in P or
> > IRML yet) so we would not be adding much more complexity; we would
> > just need to document an interface where a service can return a
> > value for P interpreter to use.
>
> Yes, I like that idea a lot. Conceptually, there is really not much
> of a difference between a P module and an OPES service. But since we
> are trying to define a framework for OPES services I think it makes
> a lot of sense to provide functions such as isGIF or findVirus as
> OPES services.  Plus, this would also allow an OPES domain
> administrator to make a local decision as to whether a service like
> isGIF should run locally on the OPES processor or on a remote
> callout server. This should be transparent to P programs, of course.
>
> I think if we decide to go down this route, then it would be a
> natural choice to restrict P module access to message headers while
> OPES services can access the entire message. Of course, a utility
> service like isGIF would only need to see the first few bytes of the
> message body and would therefore be less expensive to invoke than a
> service that needs to operate on larger parts of the message body.
> This could be one criteria that determines whether or not a service
> is a good candidate for local execution.

I am glad we seem to be making progress here. It seems to me that if
we make service interface identical to a function call interface, then
there will be no reason to distinguish the two in P! It would be up to
the module writer and service provider to decide which way to go and
when. Thus, we "solve" the problem by offloading all these
optimization decisions to those who have more information at hand.

The question is, then, how we can make a service invocation to look
identical to a module method invocation from P user point of view?
There are many similarities here:

	- both modules and services need to be selected
	  and "loaded" (imported) by a programmer

	- both module methods and services need to be
	  executed

	- interface to get method execution results
	  or service execution results is still
	  undocumented

The difference is that a module may have many methods while service
is, essentially one method (perhaps complex). That is, a module may
have many entry points while the service has only one.

The remaining difference is a perception that a function call "returns
fast" while a service execution may "take a while". Clearly, this is
not a perception worth supporting explicitly because both assumptions
may not be true.

So, how about the following short-term approach? Use import command to
load both modules and services. State that P Core does not define how
loaded modules or services provide P interpreter with a list of their
available entry points (this is currently the case as well, but
services are not loaded the way modules do). Essentially, make no
distinction between a module and a service. Everything is a module.
Service is a module, usually with a single method.

Long-term, decide whether we should document either a one-for-all
"list-of-entry-points-with-their-profiles" interface or one possible
"list-of-entry-points-with-their-profiles" interface, as a separate
draft. Note that this interface is internal to P interpreter;
programmers do not need to be aware of it.

Will something like that work?

Alex.