Hi Eduardo and Daniel,

I know you're both concerned with complexity and stack shuffling in  
recursive combinators.

Pretend for a minute you're living in la la land, where 'curry' is  
compiled efficiently, and is also fast in interpretation (by consing  
a virtual sequence instead of copying). Also pretend we have a  
'compose' word which is like 'append', but again it is only for  
quotations, and it compiles in-line.

Now, pretend the core has the following utility combinators (which  
are still ugly) -- note that in this e-mail, 'times' is different  
from the 'times' you know and love -- it passes the index to the  
quotation:

: 2nth-unsafe ( n s s -- x x )
     >r over >r nth-unsafe r> r> nth-unsafe ; inline

: 2curry curry curry ; inline

: 3curry curry curry curry ; inline

: (times) ( i quot n -- )
     pick over >= [
         3drop
     ] [
         >r 2dup 2slip >r 1+ r> r> (times)
     ] if ; inline

: times ( n quot -- )
     swap 0 -rot (times) ; inline

: (find-integer) ( i quot n -- j )
     pick over >= [
         3drop f
     ] [
         >r 2dup 2slip rot [
             r> 2drop
         ] [
             >r 1+ r> r> (find-integer)
         ] if
     ] if ; inline

: find-integer ( n quot -- ? )
     swap 0 -rot (find-integer) ; inline

: all-integers? ( n quot -- ? )
     [ not ] compose find-integer not ; inline

: (find-last-integer) ( i quot -- j )
     over 0 <= [
         2drop f
     ] [
         2dup 2slip rot [
             drop
         ] [
             >r 1- r> (find-last-integer)
         ] if
     ] if ; inline

: find-last-integer ( n quot -- ? )
     >r 1- r> (find-last-integer) ; inline

Now, take a look at the following and compare with today's sequence- 
combinators.factor:

: collect ( n quot dst -- newseq )
     [ >r keep r> set-nth-unsafe ] 2curry times ; inline

: compose ( quot1 quot2 -- seq ) append ;

: (each) ( seq quot -- n newquot )
     >r dup length swap [ nth-unsafe ] curry r> compose ; inline

: each ( seq quot -- )
     (each) times ; inline

: (map) ( seq quot -- n newquot seq )
     over >r (each) r> ; inline

: collect-new ( n quot dst -- dst )
     dup length swap new [ collect ] keep ; inline

: map ( seq quot -- newseq )
     (map) collect-new ; inline

: change-each ( seq quot -- )
     (map) collect ; inline

: (2each) ( seq1 seq2 quot -- n newquot )
     >r [ max-length ] 2keep [ 2nth-unsafe ] 2curry
     r> compose ; inline

: 2each ( seq1 seq2 quot -- )
     (2each) times ; inline

: (2map) ( seq1 seq2 quot -- n quot seq1 )
     pick >r (2each) r> ; inline

: 2map ( seq1 seq2 quot -- newseq )
     (2map) collect-new ; inline

: subset ( seq quot -- newseq )
     over >r
     over length pick new-resizable
     [ [ push-if ] 2curry each ] keep
     r> like ; inline

: all? ( seq quot -- ? )
     (each) all-integers? ; inline

: finish-find ( n seq -- n elt )
     over [ dupd nth ] [ drop f ] if ; inline

: (find) ( seq quot iterator -- n elt )
     pick >r >r (each) r> call r> finish-find ; inline

: find ( seq quot -- n elt )
     [ find-integer ] (find) ; inline

: find-last ( seq quot -- n elt )
     [ find-last-integer ] (find) ; inline

Note the absence of the following:

- 3keep
- roll
- rot (except in the integer combinators)
- tuck
- nip
- swapd
- 2swap
- only 1 dupd :)

Even more interesting is the following fact. If you single-step  
through, say,

   { 1 2 3 } [ sq ] map

you eventually end up with

   3 [ { 1 2 3 } nth-unsafe sq ] collect-new

and then

   3 [ { 1 2 3 } nth-unsafe sq ] { f f f } [ collect ] keep

and then

   [
       [ { 1 2 3 } nth-unsafe sq ] { f f f }
       >r keep r>
       set-nth-unsafe
   ] times

The quotation is built up piecemeal, then passed to 'times'. It is  
very interesting how the values end up _inside_ the quotation, which  
then operates on them.

Now, if we look at the current implementation of 'map', and inline  
some factors, we end up with the following, not nearly as pretty as  
the previous:

   { 1 2 3 } [ sq ]
   over dup length
   [ [ rot nth-unsafe swap call ] 3keep drop rot ]
   -rot tuck >r new r>
   [ [ -rot >r over slip r> set-nth-unsafe ] 3keep ] repeat
   nip 2nip

Yuck! So much unnecessary stack shuffling. Sure, the compiler  
optimizes everything away, but interpreter speed, single stepper  
usability, and code understandability suffers.

I actually did some benchmarks. Interpreted, the curry-using  
combinators are twice as fast as the current ones! There is far less  
stack shuffling going on. However they don't compile.

Finally, I think an efficient 'curry' also makes it easy to implement  
efficient lexically scoped variables, and in turn make Factor a  
better platform for hosting languages with names, such as Scheme.  
libs/locals and libs/rewrite-closures will be able to merge.

Open problem: the integer combinators at the top of the e-mail are  
still ugly.

Open problem #2: actually implement non-copying and compiling curry/ 
compose! I'm going to do that for 0.90.

Slava

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Factor-talk mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/factor-talk

Reply via email to