guix-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Adding wc to Bournish


From: Ludovic Courtès
Subject: Re: Adding wc to Bournish
Date: Thu, 26 May 2016 10:46:21 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux)

Hello!

Efraim Flashner <address@hidden> skribis:

> GSoC just started and so far I have 13 tabs from the guile manual open
> on my computer. I almost implemented `du' at the same time as `wc' but
> at the time I was getting tripped up with functions returning functions
> or returning values.

Nice!

Some comments to complement what Ricardo et al. wrote:

> +(define (wc-w-command file)
> +  (let* ((input-file (open-file file "r"))
> +         (line       (make-string 80))
> +         (word-size  (read-delimited! " \t\n" line input-file))
> +         (word-count 0))
> +    (while (not (port-closed? input-file))
> +           (if (not (zero? word-size))
> +             (set! word-count (1+ word-count)))
> +           (set! line (make-string 80))
> +           (if (not (eof-object? (peek-char input-file)))
> +             (set! word-size (read-delimited! " \t\n" line input-file))
> +             (close-input-port input-file)))
> +    word-count))

First, regarding the port not being closed, it’s a good idea to use
‘call-with-input-file’ instead of ‘open-file’: ‘call-with-input-file’
makes sure the port gets closed when its “dynamic extent” is left, that
is, regardless of whether we leave the procedure “normally” or via an
exception.

  (call-with-input-file file
    (lambda (port)
      …))

Also, imperative programming Should Be Avoided; see “Coding Style” in
the manual.  ;-)  We don’t use an i/o monad or any such thing, but
‘set!’ and string modifications is definitely frowned upon.

As Ricardo suggests, you could use ‘port->stream’ and ‘stream-fold’ to
iterate over the characters read from the port.  I suspect that’d be
rather slow though, at least on 2.0, so another option is something
like:

  (define (lines+chars port)
    ;; Return the number of lines and number of chars read from PORT.
    (let loop ((lines 1) (chars 0))
      (match (read-char port)
        ((? eof-object?)              ;done!
         (values lines port))
        (#\newline                    ;recurse
         (loop (+ 1 lines) (+ 1 chars)))
        (_                            ;recurse
         (loop lines (+ 1 chars))))))

  (define (wc-command file)
    (let-values (((lines chars)
                  (call-with-input-file file lines+chars)))
      (format #t "~a ~a ~a~%" lines chars file)))

> +(define (wc-command file)
> +  (if (and (file-exists? file) (access? file 4))

This check is not needed and is subject to a race condition (“TOCTTOU”);
just let ‘call-with-input-file’ error out if the file cannot be read.

Bonus point: catch ‘system-error’ exceptions and report the inability to
open the file in a nice user-friendly way (but really, don’t bother
about it for now.)

> +     (let* ((wc-l ((@@ (guix build bournish) wc-l-command) file))
> +            (wc-w ((@@ (guix build bournish) wc-w-command) file))
> +            (wc-c ((@@ (guix build bournish) wc-c-command) file)))
> +       (begin 
> +         (display wc-l)(display #\space)
> +         (display wc-w)(display #\space)
> +         (display wc-c)(display #\space)
> +         (display file)
> +         (newline)))))

Remember that Bournish is a compiler that compiles Bash to Scheme.
So we must distinguish the support functions that are used at run time,
such as ‘ls-command-implementation’, from what the Scheme code that the
compiler emits (compile time).

In the case of ‘ls’, when the compiler encounters ‘ls’ in the input, it
emits this code:

  ((@@ (guix build bournish) ls-command-implementation))

‘ls-command-implementation’ is the implementation that is called when we
run the compiled program.

Thus, you must similarly distinguish those two stages by providing:

  1. A ‘wc-command-implementation’ procedure that implements ‘wc’;

  2. A ‘wc-command’ procedure that emits the code that calls
     ‘wc-command-implementation’; so something like:

        (define (wc-command args)
          `((@@ (guix build bournish) wc-command-implementation)
            ,@args))

     Better yet, ‘wc-command’ could check for the presence of “-l” or
     “-c” at compile time and emit a call to the right thing.

HTH!

Ludo’.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]