Hi Petr,
you wrote:
>The first thing which was a little bit
>difficult (or confusing)
>for me, was several refinements used in
>combination for one function call.
>Which one to place first and how to
>place arguments in a proper way? Just
>look at 'open function for example.
You can call refinements in any order you wish to. Refinements associated
with arguments: The order in which you call the refinements determines the
order in which arguments are assigned to parameters:
a: func [/ref-1 par-1 /ref-2 par-2] []
a/ref-1/ref-2 arg-1 arg-2
==> par-1 references arg-1 par-2 references arg-2
a/ref-2/ref-1 arg-1 arg-2
==> par-1 references arg-2 par-2 references arg-1 because REBOL first
encountered ref-2 and therefore used the the first argument, arg-1, to bind
the parameter associated with refernce 2, par-2.
>As for refinements promotion, I don't agree with it's usefullness.
Also, consider the name clashes. And the complexity that arises from them.
If you think of all the rules you would have to introduce in order to
define when and to whom a refinement and possibly its argument are
propagated, I think you'll soon realize that REBOL would become everything
but a user friendly language.
Single Generation Or Continous Refinement Propagation?
If you want to introduce refinement propagation you will have to decide
whether it should be a single-generation or continuous propagation, i.e.
how far down the call tree do refinements get propagated?
Let us say that
func-1: [par-1] [ func-2 return block-1 ]
where:
func-2: [par-1 /ref-1 ref-par-1] [ ... ]
block-1: [ ref-1 obj-1 ]
obj-1: make object! [ref-1: none]
Now when I call
>>func-1/ref-1 a b
who is ref-1 propagated to: func-2 or block-1? How is that decided? If it
(also) propagates to block-1 I get
return block-1/ref-1
block-1/ref-1 evaluates to obj-1. Does ref-1 continue propagating? Why not?
A random break in the propagation chain?
If refinement propagation ends whenever a refinement is supported by a
function, block or object, then the programmer, by determining the name
space of the call interface of his function at the same time determines the
specifics of refinement propagation supported in this function.
So my choice of functions that I wish to call, whose refinements I wish to
expose limit my choice of words that I can use for refinements of my own
function, which call those functions. That means two things:
1. Someone who implements a function I will call in making his choices,
without knowing what the purpose of my function is, determines something
about the call interface of my function. That should not be the case. I as
the programmer of my function, alone should determine the call interface of
my function. That's not to satisfy my superego and because I am
uncooperative, it's because I know what my function is supposed to
accomplish and therefore I am in a better position to make an optimal
choice of words to be used for my function's interface.
2. The choice of words for a call interface for a function should be
motivated by whatever is most sensible. It is sensible to follow
conventions: If it is common to express the mode in which a function
handles raw data by using the refinement /binary, then all functions that
support raw data processing should use the /binary refinement to indicate
that. At the same time, these functions are very likely to call functions
which themselves also support /binary refinements.
So trying to conform with one convention - all functions that offer a raw
data procesing mode should indicate that uniformly by using a /binary
refinement - decides an altogether other issue without further reflection,
namely the issue of which refinements supported by called functions and
accessed objects and blocks are being exposed to refinement propagation.
If a programmer wishes to propagate the /binary refinement and therefore is
forced to choose a different refinement to indicate the raw processing of
data mode of his function, all of a sudden we no longer have refinement
uniformity.
The joke here is that as soon as in an effort to support refinement
propagation we lose refinement uniformity, the impact of refinement
propagation is weakened as well. What's the point of propagating
refinements, when every function expresses the same thing with a different
word? Now I have to write:
func-1/binary/my-binary/my-my-binary/my-my-my-binary
in order to propagate the raw data processing mode to all of the functions
called by func-1, whereas if a refinement is not consumed I could express
the same thing as:
func-1/binary
and all functions called by func-1 would equally be told to operate in
binary mode.
Since the usefulness of refinement propagation is strengthened by not
having it consumed by a function that supports the refinement, refinement
propagation by its own nature discloses a preference for continuous
propagation over single generation propagation.
Continuous Propagation
If propagation continues, the value returned is none, because ref-1 in
obj-1 was set to none. Is that what I expect? What if my intention was to
get obj-1 back? Then continuous propagation will make it impossible for me
to achieve my objective.
What if some of the functions/objects my function accesses require an
argument to be associated with refinement ref-1 and others don't? How do I
determine how many arguments I need to provide to the function, func-1,
when I initially call it?
When I intend to propagate a refinement to a func-2 and func-2 requires one
argument for the refinement, but func-3 is also called in func-1, func-3
happens to support the same refinement but requires three arguments? How
many arguments do I have to supply when I call func-1? 1? 3? 7? .... What
if I don't even mean for func-3 to be called with ref-1? Does it get called
with ref-1 anyway? How do I prevent that from happening?
Result:
Introducing refinement propagation to REBOL will either be of severely
limited use (single genenration one-time consumption refinements) or make
the language ridiculously complicated (continuous, recycled refinements).
Elan