Here is a major elaboration on generators (I post this as is, I have no one 
else whom I can ask for a proofread in any case):


'One concern with lazy evaluation is that errors may not be encountered'

Yes, exactly this is an excellent point: generators must be able to guarantee 
their ability to generate the entire list without error,

which leads us to a key point, which I feel is being overlooked here: 
Generators are absolutely not a replacement for evaluating all temporary 
arrays, they are a situatuational improvement ! There are naturally plenty of 
cases where holding off evaluation is detrimental:

i.3 : the physical array is clearly simpler and faster than a generator

u&.> : this generator has information about the whole array, but not 
(necessarily) about individual boxes, so perhaps this is a case where we should 
give up and create the array. illustration: u@v y: u asks for a cell, v 
produces it, and u fails on it for some reason => perhaps we overlooked an 
earlier error where v would have failed on a different cell.


Very importantly, there is nothing lost from holding off evaluation until one 
is certain it is necessary: at worst, we lose a handful of processor ticks 
trying to hold off the inevitable, and at best, we avoid building a temporary 
array and open possiblities for simplifications down the line.


The obstacle posed by accurate error reporting is a significant one however. 
For finite arrays, we can fall back to spawning arrays whenever a generator 
combination is unable to guarantee success. For the infinite case, perhaps the 
best we can do is print the error and additionally print a warning wherever a 
generator link was compiled that cannot guarantee correctness. However, 
theoretically it should be possible to predict any possible error down the line 
when creating a generator link. Generators are not really verbs.. they are 
nouns to the JFE after all, and thus have all the information necessary to be 
produced in their entirety, although some complications arise for subsequent 
generators when verifying correctness after someone asks to box parts of a 
generator. Again, my canonical example where I feel one must break a generator 
chain is when working under boxes of an existing physical array containing 
boxed elements. Fortunately, in this case the array is finite and we can simply 
create a physical array and terminate the generator train in progress, in order 
to check the entire noun's validity before proceeding.


Let's take a look at Roger Hui's examples and his challenge (the fact the array 
examples are infinite force a generator approach (or at least prevent the use 
of physical arrays)):

>a. A few interesting examples of infinite arrays:
>
>   odd =: 1+2*i._
>   even=: 2*i._
>   odd e. even
>0 0 0 0 0 ...
>
>   fib0=: 0,{:"1 ]2 x: (+%)/\ _$1x
>   fib1=: {."1 +/\@|.^:(i._) 0 1x
>   *./ fib0 -: fib1
> An implementation that can do the last three sentences would be quite an 
> accomplishment.

The first example is a good example of what generators would be exceptionally 
good at handling.

The second however, I believe clearly exposes Raul's main concern: we cannot 
fallback to creating physical infinite arrays, so we must, come what may, keep 
working with generator chains. Generator chains then, need to be studied in 
depth. (his second concern: whether the time I spend implementing this could be 
better spent elsewhere is of absolutely no concern to me; I have the immense 
luxury of still being a student).

So... Our generator is infinite, and we cannot fall back to conjuring it up 
physically. Does this mean that some operatoions are impossible, and must 
return a domain error? Operations must in any event be stored symbolically, so 
some errors may go undetected for some time (pending a rigorous proof of the 
following conjecture: 'we are able to either guarantee correctness for all 
generator links based on i._, or bail out immediately with an error'). Here is 
perhaps our biggest problem then: Are there any errors that cannot be predicted 
by a generator link on i._ ? Well, boxes aside, we should be able to see coming 
all syntax, rank and length errors should be predictable for any generator 
link, since we can analyze the complete information we have for generating the 
array. As a result then, we need this axiom (for a valid generator): it must 
guarantee it's ability to generate an array (short of system limits)). Should 
my conjecture about guaranteeing correctness on i._, prove false, then the 
offending operations on infinite generators must trigger a domain error.


Let's be more concrete though, and examine the generator chain for Roger's 
fib0. Noting that Generators are a natural way of representing conjunctions, 
the process I am about to describe can be thought of as 'compiling a J sentence 
to a generator chain'. I write 'Gn' to designate the nth generator, counting 
from the right.

_$1x : G1 is the constant function 1:, for any y (note it is absolutely not 
equivalent to 1:, the generator should be thought of as a noun (is a noun to 
the JFE)).

\             G2 returns (i.y) {. G1 (here we could even think of simplifying 
directly to y$1, since G1 knows it is constant)

/             y { G3 is (y, 1+y) { G2

(+ %)     y { G4 is given by  (+%)/ y {G3 (y { G3 returns 2 Values here, and G4 
is a dyadic generator)

2 x:        y { G5 returns 2 x: y { G4

{:"1        y { G6 returns {:"1 y { G5 NB. Here is potentially a win, as G6 
won't ask for all of G1-5

0,           y { G7 is represented as a concatenation of a physical list and a 
generator.

The last generator would usually be forced into a physical array.. this array 
being infinite though, I suggest eitheir laconically printing a prefix, as J 
usually does, or even entering an infinite loop printing loop if one uses ,. 
may be a valid reaction. Naturally some operations on an infinite generator 
must trigger a domain error: attempting to print the thing to a file comes to 
mind. Now we see also the dream take shape: result argument cells can be built 
directly from the rightmost argument! Those generators that intend to reuse 
argument cells can be expected to remember them, in order to avoid asking for a 
recalculation. (or perhaps such a generator link should force an array).


'fib1' and 'fb0 -: fb1' can be built similarly (u^:(i._) even maps very nicely 
to generators, the one representing ^: can readily reuse it's preceeding 
result) . Now the key to the 'accomplishment' lies in the pattern '*/ 
(generator)'. This attempt to collapse an infinite generator could of course 
just bail out with a domain error, but more interestingly, is there any 
conceivable way that this reqeust could be met ? In the specific case of 
boolean *./, one must somehow prove that the generator is in fact equivalent to 
the constant generator _$1. This suggests we need to be able to simplify 
generator chains. The good news is that there are no precedence issues in 
generator chains and J is free to rearrange commutative generators and 
hopefully remove those that cancel each other out. The actual problems 
associated with this depend on generator implementation details. If such 'J 
symbolic algebra' is possible, then at least a subset of such problems can be 
solved.

J4

________________________________
From: Source <source-boun...@forums.jsoftware.com> on behalf of Don Guinn 
<dongu...@gmail.com>
Sent: Wednesday, February 28, 2018 5:41:51 PM
To: Source forum
Subject: Re: [Jsource] Propositions

One concern with lazy evaluation is that errors may not be encountered
until something happens to make the evaluation go beyond what has been
encountered before. Particularly during development and testing. Is there a
way to turn off lazy evaluation in python during development and testing?

On Feb 28, 2018 8:43 AM, "Raul Miller" <rauldmil...@gmail.com> wrote:

> On Wed, Feb 28, 2018 at 10:21 AM, james faure <james.fa...@epitech.eu>
> wrote:
> > For J, where so much more is happening per statement, we could
> transparently replace all intermediate results by generators, except where
> a full noun is required.
>
> Worth considering in this context:
>
>    $
>    {
>    |.
>    %.
>
> > To translate the python statement to j, one would write (<./ 1 2 3 4 , 0
> 10 0 10), however we lose lots of time to python here due to calculating
> the intermediate catenation (unless we are fortunate enough that the
> interpreter has a Special Combination (SC) for this)
>
> How much is "a lot"?
>
> > For everyone who may be of the opinion that this is not worth it, I
> would like to direct your attention to the existing jsources, where vast
> amounts of effort for far smaller optimizations are apparent.
>
> As a general rule, J only accepts optimizations where there's a useful
> problem where that optimization produces at least a factor of 2
> improvement. (Often enough it's more like factor of 1000.)
>
> > for example p.c, where the parser itself tries to emplace arrays and
> avoid unnecessary copying. It is still unable to do this consistently
> however, as simple memory tests like 7!:2 'i.1000x' ,: '>: i.1000x' prove.
> (and yes I like using x for my benchmarks due to how much slower they are)
>
> I am not sure what you're talking about here - I took a quick look at
> p.c but most of what I saw was implicit in the definitions. I'm not
> comfortable calling that "optimizations".
>
> > another example is. jt.h, where a JFE's state information structure has
> been lovingly crafted to optimize it's use of cpu cache
> > or again the countless optimizations in the primitives, like n&i. hash
> tables, the immensely flexible /: etc..
>
> The hash tables aren't so good on large arrays, unfortunately - so
> that's something that could maybe use some good insights.
>
> But the thing that worries me is how many things would break if you
> tried to throw generators at them. That winds up being cases where
> generators would slow things down (because you have the work of
> building the generator and then interpreting the generator and you pay
> in both code bloat and cache pressure and in cpu pipeline breakage and
> of course any extra tests to keep things from breaking.)
>
> Put different: arrays are not "an optimization" in J. They are J's
> fundamental data structure.
>
> Or, for a similar perspective:
>
>   gnu gmp's bignum library looks like a nice-to-have
>
>   the lapack library is a nice-to-have
>
>   But integrating both of them would not solve the hard issues
> associated with using them together.
>
> Also, thinking back to infinities: one useful tactic for implementing
> math which involves infinities is to work in the reciprocal domain.
> But - hypothetically speaking - there are potentially an infinite
> number of different ways to work with infinities.
>
> Thanks,
>
> --
> Raul
> ----------------------------------------------------------------------
> For information about J forums see http://www.jsoftware.com/forums.htm
J Forums<http://www.jsoftware.com/forums.htm>
www.jsoftware.com
Forums. The J forum mailing lists give life to the J community. They are the 
best way to get help, help others, report bugs, and share your interest in J.


----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm
J Forums<http://www.jsoftware.com/forums.htm>
www.jsoftware.com
Forums. The J forum mailing lists give life to the J community. They are the 
best way to get help, help others, report bugs, and share your interest in J.

----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm

Reply via email to