From mboxrd@z Thu Jan 1 00:00:00 1970 From: ludo@gnu.org (Ludovic =?utf-8?Q?Court=C3=A8s?=) Subject: Re: Adding wc to Bournish Date: Thu, 26 May 2016 10:46:21 +0200 Message-ID: <87wpmhi4v6.fsf@gnu.org> References: <20160524184720.GA27449@debian-netbook> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:48189) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1b5qw9-0004zv-6J for guix-devel@gnu.org; Thu, 26 May 2016 04:46:40 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1b5qw4-00068G-P7 for guix-devel@gnu.org; Thu, 26 May 2016 04:46:34 -0400 In-Reply-To: <20160524184720.GA27449@debian-netbook> (Efraim Flashner's message of "Tue, 24 May 2016 21:47:20 +0300") List-Id: "Development of GNU Guix and the GNU System distribution." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-devel-bounces+gcggd-guix-devel=m.gmane.org@gnu.org Sender: "Guix-devel" To: Efraim Flashner Cc: guix-devel@gnu.org Hello! Efraim Flashner 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=E2=80=99s a good idea to use =E2=80=98call-with-input-file=E2=80=99 instead of =E2=80=98open-file=E2=80= =99: =E2=80=98call-with-input-file=E2=80=99 makes sure the port gets closed when its =E2=80=9Cdynamic extent=E2=80=9D i= s left, that is, regardless of whether we leave the procedure =E2=80=9Cnormally=E2=80=9D= or via an exception. (call-with-input-file file (lambda (port) =E2=80=A6)) Also, imperative programming Should Be Avoided; see =E2=80=9CCoding Style= =E2=80=9D in the manual. ;-) We don=E2=80=99t use an i/o monad or any such thing, but =E2=80=98set!=E2=80=99 and string modifications is definitely frowned upon. As Ricardo suggests, you could use =E2=80=98port->stream=E2=80=99 and =E2= =80=98stream-fold=E2=80=99 to iterate over the characters read from the port. I suspect that=E2=80=99d 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 (=E2=80=9CTOCTT= OU=E2=80=9D); just let =E2=80=98call-with-input-file=E2=80=99 error out if the file canno= t be read. Bonus point: catch =E2=80=98system-error=E2=80=99 exceptions and report the= inability to open the file in a nice user-friendly way (but really, don=E2=80=99t 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=20 > + (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 =E2=80=98ls-command-implementation=E2=80=99, from what the Scheme c= ode that the compiler emits (compile time). In the case of =E2=80=98ls=E2=80=99, when the compiler encounters =E2=80=98= ls=E2=80=99 in the input, it emits this code: ((@@ (guix build bournish) ls-command-implementation)) =E2=80=98ls-command-implementation=E2=80=99 is the implementation that is c= alled when we run the compiled program. Thus, you must similarly distinguish those two stages by providing: 1. A =E2=80=98wc-command-implementation=E2=80=99 procedure that implement= s =E2=80=98wc=E2=80=99; 2. A =E2=80=98wc-command=E2=80=99 procedure that emits the code that calls =E2=80=98wc-command-implementation=E2=80=99; so something like: (define (wc-command args) `((@@ (guix build bournish) wc-command-implementation) ,@args)) Better yet, =E2=80=98wc-command=E2=80=99 could check for the presence = of =E2=80=9C-l=E2=80=9D or =E2=80=9C-c=E2=80=9D at compile time and emit a call to the right thin= g. HTH! Ludo=E2=80=99.