To get some idea what's going on here:

In Haskell, all functions are pure and arguments are lazily evaluated.

There are two ways to do lazy evaluation:

(a) substitution: replace the parameter by the argument in a cloned 
function body and evaluated that. Cloning the body can generate
a new function we call, or we could inline it for even further
performance gain. 

Substitution is the fastest method provided the argument
isn't used many times.

(b) Wrap the argument in a closure and evaluate that when required.
This is sometimes sped up by caching the result.
This is generally SLOW.

In order to improve performance, Haskell defines the
concept of strictness. If a function isn't total, it can
bug out. If a function takes an argument which bugs out,
and the function also necessarily bugs out too, the function
is said to be strict (in that argument). This is written

        f (bug) = bug

where bug is called bottom and isn't a real value.

The reason strictness is important is this: if a function
is strict in an argument, then if the argument is going
to bug out, it doesn't matter whether it bugs out by
being evaluated before calling the function or after.

So if a function is strict, we can use eager evaluation
without changing the semantics.

Eager evaluation is useful in two cases where lazy
evaluation is slow:

(a) substitution: many uses of the argument cause
multiple evaluations.

(b) closures, when the argument can just be passed
in a variable (instead of itself being a closure to ensure
it isn't evaluated too early).

So we have: if a function is pure and strict then 
indeterminate evaluation has no semantic impact
and is clearly optimal because the compiler can choose.

So these then are sufficient conditions for a "val" in Felix too.
The initialiser has to be composed of pure strict functions.
In that case it doesn't matter when the evaluation is done.

however, these conditions are too .. ah .. strict.
Its fine to refer to a variable if the value is the same with
either evaluation strategy. In particular, if the variable
has its final value before the earliest possible evaluation
of the val, or, at least doesn't change until after the last
possible use. So as long as the variable is invariant
in the potential evaluation range, that's enough.

I could make some words here such as: if the variable
changes that extinguishes the val that depends on it.

Felix current checks for extinguishment (but not initialisation!)
When it sees a sequence of vals,

        val x =  ..
        val y = ..
        val z = ...

is substitutes x into y and z, and y into z in an attempt to
eliminate x and y. Variables waste space in objects.
[Recall Felix functions and procedures are notionally
C++ objects]

It checks how many uses there are too: if there are more
than 2 it doesn't substitute, it keeps the variable (eager
evaluation).

When it sees a label, a goto, and perhaps a variable
assignment or procedure call it stops substituting.

If it substituted all occurrences of the variable
a subsequent garbage collection pass will eliminate
the variable -- unless its a parameter which is really
tricky!

However there's no strictness check. one might be added,
at the cost of having to annotate all C function bindings,
to tell if they're strict. Then perhaps Felix might be able
to tell if a function derived from these is strict.

However, the result wouldn't be optimal because Felix
would have to be conservative. At present, Felix is optimal
because instead it is aggressive (it acts as if functions are
strict).

Note you can always ensure lazy evaluation (at the moment
anyhow) by explicitly passing a closure. There are some other
tricks too: mark the function noinline to ensure the function
is a closure and you'll get eager evaluation of the arguments

With a couple of caveats such as if the parameter isn't
used and the function is called directly. This bites
if the argument is a generator: such generator calls
can then be elided, losing their side effects.

be warned of that, it applies to variables too:

        var x = g 1;

will do nothing, no matter what g does, because x isn't used.
Fix it by:

        C_hack::ignore (g 1);

Note: this is desirable! You can safely make a pool of worker
threads and if you don't use it, it doesn't exist.

Anyhow just as an example the C function:

        c ? t : f

is strict in c, but not t or f.  Similarly

        a && b

is strict in a but not b. OTOH

        a,b 

is strict in both.

--
john skaller
skal...@users.sourceforge.net
http://felix-lang.org




------------------------------------------------------------------------------
LogMeIn Rescue: Anywhere, Anytime Remote support for IT. Free Trial
Remotely access PCs and mobile devices and provide instant support
Improve your efficiency, and focus on delivering more value-add services
Discover what IT Professionals Know. Rescue delivers
http://p.sf.net/sfu/logmein_12329d2d
_______________________________________________
Felix-language mailing list
Felix-language@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/felix-language

Reply via email to