[racket-dev] in-file-lines sequence

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Fri Jan 21 15:37:20 EST 2011

At Fri, 21 Jan 2011 10:53:42 -0700, Doug Williams wrote:
> What is the best way to perform actions after a sequence terminates? Here is
> a simple example that is like in-lines, but takes a path instead of a port.
> It works, but I was wondering if there is a better way to do it.
> (define (in-file-lines path)
>   (let ((port (open-input-file path #:mode 'text)))
>     (make-do-sequence
>      (lambda ()
>        (values
>         (lambda (_) (read-line port 'any))
>         void
>         (void)
>         void
>         (lambda _ (if (eof-object? (peek-byte port))
>                       (begin
>                         (close-input-port port)
>                         #f)
>                       #t))
>         void)))))

I think you mean

 (define (in-file-lines path)
   (let ((port (open-input-file path #:mode 'text)))
      (lambda ()
         (lambda (_) (read-line port 'any))
         (lambda (v) (if (eof-object? v) ; <----------
                           (close-input-port port)

so that the last line is included in the sequence. Otherwise, I don't
have any better suggestions.

Beware that if the sequence stops being used before you get to the end
of the file, then the port won't be closed:

 (for ([line (in-file-lines <file>)]
       [elem (in-list '(1 2 3))])
 ; port isn't closed if it has more than 3 lines

But the sequence protocol doesn't give a sequence any way to know that
it isn't being used, other than GC-based finalization --- which is
probably a bad idea for file ports, since you can run out of available
file descriptors much faster than available memory.

Posted on the dev mailing list.