unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* Announcing the first actually stable release of guile-for-loops
@ 2020-01-23 12:10 Linus Björnstam
  2020-01-24  8:16 ` Arne Babenhauserheide
  0 siblings, 1 reply; 4+ messages in thread
From: Linus Björnstam @ 2020-01-23 12:10 UTC (permalink / raw)
  To: guile-user

Hiya everybody!

I have spent some time implementing efficient for loops for guile, and they are baked and ready to go. I have worked the last weeks at implementing generalized support for non-tail-recursive loops and am happy to announce for/foldr. It is a generic right fold, with support for delaying it's arguments as either thunks or promises. 

The syntax is more or less the same as racket's loops, and they are generally compatible. The code generated is for almost all cases as fast as hand-rolled code. They are all expressed as left or right folds, and are as such (apart from for/list, but read about that in the documentation) free of mutation. They are all converted to named lets. 

Some examples:

(for/list ((a (in-range 1 6)))
  (* a a)) ;; => (1 4 9 16 25)

(for*/list ((a (in-string "ab")) (b (in-range 1 3)))
  (list a b)) 
;; => ((#\a 1) (#\a 2) (#\b 1) (#\b 2))

There are many more looping constructs, among others: 
for/sum, for/vector, for/or, for/and, for/first, for/last and a side-effecting simple for.

Here is a sieve of erathostenes:

(define (erathostenes n)
  (define vec (make-vector n #t))
  (for/list ([i (in-range 2 n)] #:when (vector-ref vec i))
    (for ([j (in-range/incr (* 2 i) n i)])
      (vector-set! vec j #f))
    i))

The code and documentation is available here: 
https://hg.sr.ht/~bjoli/guile-for-loops

A web-friendly documentation can be found here: 
https://man.sr.ht/%7Ebjoli/for-loops-docs/for-loops.md

The thing I had been waiting for is right fold. That allows us to write loops like guile's map: non-tail recursive:
(for/foldr ((identity '())) ((a (in-list '(1 2 3))))
  (cons (* a a) identity))

becomes equivalent to:

(let loop ((random-identifier '(1 2 3)))
  (if (null? random-identifier)
      '()
      (let ((a (car random-identifier)))
        (cons (* a a) (loop (cdr random-identifier))))))

Happy hacking
Linus Björnstam



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Announcing the first actually stable release of guile-for-loops
       [not found] <mailman.81.1579798810.6198.guile-user@gnu.org>
@ 2020-01-23 23:22 ` Zelphir Kaltstahl
  0 siblings, 0 replies; 4+ messages in thread
From: Zelphir Kaltstahl @ 2020-01-23 23:22 UTC (permalink / raw)
  To: guile-user

Hi Linus!

Although I just ported multiple usages of those kind of loops from a
previously Racket project to a Guile project, I think this is quite
cool! When I ported those usages in my code, it also resulted in some
named lets (I guess quite naturally, as an available looping construct),
so I can relate to that.

Thanks!

Regards,

Zelphir

On 1/23/20 6:00 PM, guile-user-request@gnu.org wrote:
> Message: 4
> Date: Thu, 23 Jan 2020 13:10:46 +0100
> From: Linus Björnstam <linus.internet@fastmail.se>
> To: guile-user@gnu.org
> Subject: Announcing the first actually stable release of
> 	guile-for-loops
> Message-ID: <f536f849-5266-40d6-a3dc-f815834a0d30@www.fastmail.com>
> Content-Type: text/plain;charset=utf-8
>
> Hiya everybody!
>
> I have spent some time implementing efficient for loops for guile, and they are baked and ready to go. I have worked the last weeks at implementing generalized support for non-tail-recursive loops and am happy to announce for/foldr. It is a generic right fold, with support for delaying it's arguments as either thunks or promises. 
>
> The syntax is more or less the same as racket's loops, and they are generally compatible. The code generated is for almost all cases as fast as hand-rolled code. They are all expressed as left or right folds, and are as such (apart from for/list, but read about that in the documentation) free of mutation. They are all converted to named lets. 
>
> Some examples:
>
> (for/list ((a (in-range 1 6)))
>   (* a a)) ;; => (1 4 9 16 25)
>
> (for*/list ((a (in-string "ab")) (b (in-range 1 3)))
>   (list a b)) 
> ;; => ((#\a 1) (#\a 2) (#\b 1) (#\b 2))
>
> There are many more looping constructs, among others: 
> for/sum, for/vector, for/or, for/and, for/first, for/last and a side-effecting simple for.
>
> Here is a sieve of erathostenes:
>
> (define (erathostenes n)
>   (define vec (make-vector n #t))
>   (for/list ([i (in-range 2 n)] #:when (vector-ref vec i))
>     (for ([j (in-range/incr (* 2 i) n i)])
>       (vector-set! vec j #f))
>     i))
>
> The code and documentation is available here: 
> https://hg.sr.ht/~bjoli/guile-for-loops
>
> A web-friendly documentation can be found here: 
> https://man.sr.ht/%7Ebjoli/for-loops-docs/for-loops.md
>
> The thing I had been waiting for is right fold. That allows us to write loops like guile's map: non-tail recursive:
> (for/foldr ((identity '())) ((a (in-list '(1 2 3))))
>   (cons (* a a) identity))
>
> becomes equivalent to:
>
> (let loop ((random-identifier '(1 2 3)))
>   (if (null? random-identifier)
>       '()
>       (let ((a (car random-identifier)))
>         (cons (* a a) (loop (cdr random-identifier))))))
>
> Happy hacking
> Linus Björnstam



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Announcing the first actually stable release of guile-for-loops
  2020-01-23 12:10 Linus Björnstam
@ 2020-01-24  8:16 ` Arne Babenhauserheide
  2020-01-24 21:02   ` Linus Björnstam
  0 siblings, 1 reply; 4+ messages in thread
From: Arne Babenhauserheide @ 2020-01-24  8:16 UTC (permalink / raw)
  To: guile-user

Hi,

Linus Björnstam <linus.internet@fastmail.se> writes:
> The syntax is more or less the same as racket's loops, and they are generally compatible. The code generated is for almost all cases as fast as hand-rolled code. They are all expressed as left or right folds, and are as such (apart from for/list, but read about that in the documentation) free of mutation. They are all converted to named lets.

That’s cool!

> (define (erathostenes n)
>   (define vec (make-vector n #t))
>   (for/list ([i (in-range 2 n)] #:when (vector-ref vec i))
>     (for ([j (in-range/incr (* 2 i) n i)])
>       (vector-set! vec j #f))
>     i))
>
> The code and documentation is available here:
> https://hg.sr.ht/~bjoli/guile-for-loops

Is there a chance that this could get included in Guile (ice-9
for-loops?) and become a SRFI?

If the code is non-portable (I guess the #:keywords are), the two
existing implementations (Racket, Guile) would still be sufficient for a
SRFI.

Best wishes,
Arne
--
Unpolitisch sein
heißt politisch sein
ohne es zu merken



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Announcing the first actually stable release of guile-for-loops
  2020-01-24  8:16 ` Arne Babenhauserheide
@ 2020-01-24 21:02   ` Linus Björnstam
  0 siblings, 0 replies; 4+ messages in thread
From: Linus Björnstam @ 2020-01-24 21:02 UTC (permalink / raw)
  To: Arne Babenhauserheide, guile-user

On Fri, 24 Jan 2020, at 09:16, Arne Babenhauserheide wrote:
> Hi,
> 
> Linus Björnstam <linus.internet@fastmail.se> writes:
> > The syntax is more or less the same as racket's loops, and they are generally compatible. The code generated is for almost all cases as fast as hand-rolled code. They are all expressed as left or right folds, and are as such (apart from for/list, but read about that in the documentation) free of mutation. They are all converted to named lets.
> 
> That’s cool!
> 
> > (define (erathostenes n)
> >   (define vec (make-vector n #t))
> >   (for/list ([i (in-range 2 n)] #:when (vector-ref vec i))
> >     (for ([j (in-range/incr (* 2 i) n i)])
> >       (vector-set! vec j #f))
> >     i))
> >
> > The code and documentation is available here:
> > https://hg.sr.ht/~bjoli/guile-for-loops
> 
> Is there a chance that this could get included in Guile (ice-9
> for-loops?) and become a SRFI?

John Cowan asked me to write a SRFI, but right now I don't think it is really in good enough shape to be a srfi. The for/foldr internals is a can of worms. Currently I am planning on removing the "procedural" part of that (stolen from what I saw in the macro expansion of racket's for/foldr) and resort to macros generating macros instead (due to otherwise losing syntax context information).

I could probably trim at least 100 lines of code from the implementation as it is now.

> 
> If the code is non-portable (I guess the #:keywords are), the two
> existing implementations (Racket, Guile) would still be sufficient for a
> SRFI.

The code is surprisingly portable as long as you are porting to a syntax-case scheme! Redefining the keywords as auxiliary syntax made porting an earlier version to chez scheme a 2 evening job :) I still had some annoying bugs that I didn't want to spend time on, but it shouldn't be hard. Some of the recent changes to the code was introduced to facilitate porting to non-keyword schemes: (in-range a b #:incr c) => (in-range/incr a b c), for example.

The only downside is that I would have to squat on when:, unless:, final:, break:, length: and fill:.

/Linus



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2020-01-24 21:02 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <mailman.81.1579798810.6198.guile-user@gnu.org>
2020-01-23 23:22 ` Announcing the first actually stable release of guile-for-loops Zelphir Kaltstahl
2020-01-23 12:10 Linus Björnstam
2020-01-24  8:16 ` Arne Babenhauserheide
2020-01-24 21:02   ` Linus Björnstam

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).