Felix currently doesn't allow side effects in functions,
which sometimes forces you to write crud like this:

var x: string;
readline &x;

print$ strip x;

instead of

print$ strip(readline ());

as you might in C. This is because readline is a procedure
with side-effects.

To ameliorate this situation you can write:

var &x : string <- readline;

which kind of looks like a function call, even though
it is only sugar for calling a procedure whose first
argument is a pointer.

Another case is where you WANT a procedure, but
it might fail:

var retcode: bool;
tryit &retcode;
if retcode do .. done;

but in C you'd just do:

if tryit() do .. done;

I now have some hacks implemented which would allow
the constraints to be fixed. This works with my hack:

-------------------------------------
#import <flx.flxh>

proc f(ref x:int,b:int) {
  x = 22;
}

fun g(x:int)=>x + x;
var z = 1;
print z; endl;
z = g(f 33);
print z; endl;
----------------------------

Here f is a procedure with a ref as the first argument:
recall that's sugar for a pointer. My hack only works
with refs, to avoid breaking existing code.

The procedure f has procedural signature:

        &int * int -> void

but it can also be used as a function with signature:

        int -> int

that is, the first argument, being a pointer, can
also be used to return a value. Hence the application

        f 33

is permitted. The implementation of

        z = g(f 33);

is of course:

        var tmp : int;
        f (&tmp, 33);
        z = f tmp;

that is, the procedure calls are lifted out of the
expression. Generally we expect C to do the same:
a C expression is notionally reduced to procedural
code by transforming it to SSA (single assignment)
form:

        a = f (b + c) * d;

is reduced to

        tmp1 = b + c;
        tmp2 = f(tmp2);
        a = tmp2 * d;

All the C rules about sequence points constrain the ordering
of these simpler assignments .. and possibly whether some
of them might be done in parallel, or otherwise interleaved,
rather than some strict ordering.

I call this 'unravelling', and each step of the reduction
I term 'lifting'.

In the Felix code, lifting the procedural applications
out leaves the expression transparent again, that is,
there are no side-effects in the expression.

Note in particular, with this stuff the original expression
is not transparent, and could never be executed by Felix
anyhow, since procedures and functions use radically different
computational models: procedures are stackless, and execution
can be interleaved, they can exchange control and even do
asynchronous I/O and otherwise call the Felix 'operating
system'. Functions cannot do that (because it would require
machine stack swapping hacks).

The ability to use side-effects like this will please
some people and not others. I am not happy.. but I'm
not happy with the manually unravelled form either.

One can argue that the indeterminacy of order of side
effects is NOT the issue here. C sequence points allow
you to manage that and unravel manually if necessary.
Felix can dictate, for example, the side effects
will be in order of writing (although that may not
be entirely obvious with sugaring involved).

Or, some expressions simply have only one side-effect
producing action, so order isn't an issue; similarly
it may have multiple non-interacting side effects,
or, even with interacting side effects we don't actually
care what the outcome is.

All of these things are quite common cases -- otherwise
what C does would be entirely untenable .. instead of
the feature actually being one of the most heavily
used facilities of the language.

But I am NOT happy still. In the example code,
at least the side-effect producing routine is labelled
as a procedure, not a function. At least the programmer
declared it has side effects. This is good, although
there is a downside that it isn't clear writing through
the pointer is the only one: in this procedure:

        proc incr(&x:int){ x = x+1; }

that is the case -- although there are side effects
here, the effect is entirely an output through an
explicit parameter .. the procedure doesn't modify
an global variables, for example. The procedure
is 'pure' in this sense.

Furthermore the loss of transparency is very heavy,
and the conversion of procedure type to a function
is implicit.

I am thinking to make it *explicit*, and maintain
transparency by something like:


        z = g [! f 33 !];

Here the encoding explicitly transforms the procedure to
a function and tells the compiler to lift the application.
Because the 'marking' is explicit sugar, referential
transparency is preserved.

You should note that at the moment Felix already does some
unravelling. However it doesn't go all the way, and the
intent is not semantic -- it is done simply so it can
generates shorter lines of C++ just to make the C++ more
readable. The lift above is mandatory.

There are actually several things being done here, so the
notation is still open. Lifting could be applied to
C primitives as well, quite independently to allowing
a procedure to be overloaded so it could be used in an
expression context.

We could also do something more radical: change the type
system to recognize side effects:

        D -> C // function
        D ->> C // function depending on variables
        D ->>> C // function with side effects

Here the notion of function is split into 3 categories:
the first kind is pure and gives the same result
no matter when it is evaluated, the second is implicitly
coupled to variables, so the time of evaluation matters
(but not in purely functional code .. because variables cant
change in that kind of code), and the third actually
has side effects, so is basically a procedure which can
also return values.

In C, all functions have the last form (unless the
optimiser can deduce otherwise). In Haskell all have
the first form.

This notation is still doesn't count for divergence
(a function that can error out depending perhaps
on where it is elaborated, which can be OK
if, in the divergent cases, it is never executed).

Using the type system is probably the right way to go,
its just the hardest to implement and adds considerably
to complexity. OTOH it may simplify some code which currently
has to lookup things to find out properties.

Note that the notations are STILL weak abstractions.
For example, a more properly monadic 'function depends
on variables' would actually say WHICH variables,
and that increases the scope where the optimiser is
free to place elaborations.

-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Felix-language mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/felix-language

Reply via email to