unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
* pass at srfi-89 implementation
@ 2008-05-03  3:37 Julian Graham
  2008-05-05 21:43 ` Ludovic Courtès
  2008-05-19 20:15 ` Ludovic Courtès
  0 siblings, 2 replies; 15+ messages in thread
From: Julian Graham @ 2008-05-03  3:37 UTC (permalink / raw)
  To: guile-devel

[-- Attachment #1: Type: text/plain, Size: 2047 bytes --]

Hi everyone,

So I've taken a stab at implementing SRFI-89, using Guile's existing
(ice-9 optargs) module -- my thinking was that the two were similar
enough to make it worthwhile to have optargs do most of the heavy
lifting.  What I've done is add some pre-parsing of the formals list
before it's passed to (ice-9 optargs)'s lambda* and some accommodation
of incompatible behavior.  Specifically:

* The (ice-9 optargs) module requires that the #optionals section, if
present, come before the #keywords section, whereas SRFI-89 allows its
corresponding sections to be in either order.

* (ice-9 optargs) requires that non-optional positional formals be
specified before any optional positional or keyword formals.

* (ice-9 optargs) (and, apparently, Common Lisp) adds all the keyword
arguments to the rest argument at call time.

* SRFI-89 doesn't allow duplicate keyword arguments in a function call.

* SRFI-89 keyword definitions allow the value of a keyword argument to
be bound to a variable with a different name than the keyword; (ice-9
optargs) does not.

I think that's everything -- except for one last quirk of (ice-9
optargs) that I discovered last night and am having trouble working
around.  It looks like when you've defined a function that takes
traditional/required as well as keyword arguments, you need to pass
all of the required arguments before passing any of the keyword ones.
For example:

(define* (g a #:key (b #f) #:rest r) (list a b r))

(g 1 #:b 2 3) => (1 2 (3))

...but

(g #:b 2 1 3) => (#:b #f (2 1 3))

The docs sort of hint at why this is implemented the way it is, but I
think it eliminates a lot of the flexibility that keyword arguments
buy you in the first place.  What I want to know is whether this seems
to you like a shortcoming in (ice-9 optargs) that ought to be fixed,
or whether I should just ditch my implementation and go with the
vanilla SRFI-89 implementation included with the specification.

I've attached the draft I've been working on in case anyone wants to
have a look.


Regards,
Julian

[-- Attachment #2: srfi-89.scm --]
[-- Type: application/octet-stream, Size: 3657 bytes --]

;;; srfi-89.scm --- Optional positional and named parameters

;; Copyright (C) 2008 Free Software Foundation, Inc.
;;
;; This library is free software; you can redistribute it and/or
;; modify it under the terms of the GNU Lesser General Public
;; License as published by the Free Software Foundation; either
;; version 2.1 of the License, or (at your option) any later version.
;;
;; This library is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;; Lesser General Public License for more details.
;;
;; You should have received a copy of the GNU Lesser General Public
;; License along with this library; if not, write to the Free Software
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

(define-module (srfi srfi-89)
  #:use-module (ice-9 optargs)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-8)
  #:use-module (srfi srfi-88)
  #:replace (define* lambda*))

(define-macro (define* pattern . body)
  (if (pair? pattern)
      `(define ,(car pattern) (lambda* ,(cdr pattern) ,@body))
      `(define ,pattern ,@body)))

(define-macro (lambda* formals . body)
  (define (transform-formals lst ht)
    (define (transform-pairs lst)
      (define (format-lists named optional)
	(append '() 
		(if (null? optional) '() (cons #:optional optional))
		(if (null? named) '() (cons #:key named))))
      (define (car-is-keyword? x) (keyword? (car x)))
      (define (not-car-is-keyword? x) (not (car-is-keyword? x)))
      (define (save-binding x)
	(let ((cx (string->symbol (keyword->string (car x)))))
	  (hashq-set! ht cx (cadr x)) 
	  (cons cx (cddr x))))
      (define (parse-formals lst keywords-first)
	(receive (l1 l2)
		 ((if keywords-first span break) car-is-keyword? lst)
		 (if (or (find car-is-keyword? (if keywords-first l2 l1))
			 (find not-car-is-keyword? (if keywords-first l1 l2)))
		     (error "Ordering error in formal parameters list")
		     (format-lists (map save-binding (if keywords-first l1 l2))
				   (if keywords-first l2 l1)))))
	
      (if (null? lst) '() (parse-formals lst (car-is-keyword? (car lst)))))
  
    (receive (other-formals req-positionals)
	     (partition pair? lst)
	     (append req-positionals (transform-pairs other-formals))))

  (define (formals+rest l)
    (define (inner h t)
      (if (pair? t) (inner (append h (list (car t))) (cdr t)) (values h t)))
    (inner '() l))

  (receive 
   (n-r r)
   (formals+rest formals)  
   (let* ((ht (make-hash-table))
	  (rest (if (not (null? r)) r 'srfi-89:rest-var))
	  (tf (append (transform-formals n-r ht) (list #:rest rest)))	  
	  (kws (hash-map->list cons ht)))
     
     `(let ((receive (@ (srfi srfi-8) receive))

	    (let-keywords* (@ (ice-9 optargs) let-keywords*))
	    (let-optional* (@ (ice-9 optargs) let-optional*)))
	((@ (ice-9 optargs) lambda*) ,tf

	 (define (srfi-89:rest r)
	   (define count (@ (srfi srfi-1) count))
	   (define delete-duplicates (@ (srfi srfi-1) delete-duplicates))
	   (define split-at (@ (srfi srfi-1) split-at))
	   
	   (receive (ks rs)
		    (split-at r (* (count keyword? r) 2))
		    (let ((kws (filter keyword? ks)))
		      (if (eq? kws (delete-duplicates kws))
			  rs (error "Duplicate keyword binding")))))
	 
	 ,(if (not (null? r))
	       `(receive
		 ,(append (map cdr kws) `(,rest))
		 ,(cons 'values (append (map car kws) `((srfi-89:rest ,rest))))
		 ,@body)
	       `(receive ,(map cdr kws)
			 ,(cons 'values (map car kws))
			 (begin (or (null? (srfi-89:rest ,rest))
				    (error "Wrong number of argument"))
				,@body))))))))

[-- Attachment #3: srfi-89.test --]
[-- Type: application/octet-stream, Size: 2520 bytes --]

;;;; srfi-89.test --- Test suite for SRFI-89              -*- Scheme -*-
;;;; Julian Graham <julian.graham@aya.yale.edu>
;;;;
;;;; 	Copyright (C) 2008 Free Software Foundation, Inc.
;;;;
;;;; This program is free software; you can redistribute it and/or modify
;;;; it under the terms of the GNU General Public License as published by
;;;; the Free Software Foundation; either version 2, or (at your option)
;;;; any later version.
;;;;
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;;; GNU General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU General Public License
;;;; along with this software; see the file COPYING.  If not, write to
;;;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;;;; Boston, MA 02110-1301 USA

(define-module (test-srfi-89)
  :use-module (test-suite lib)
  :use-module (srfi srfi-89))

\f
;; These are all taken from SRFI-89.

(define* (f a (b #f)) (list a b))
(define* (g a (b a) (key: k (* a b))) (list a b k))
(define* (h1 a (key: k #f) . r) (list a k r))
(define* (h2 (key: k #f) a . r) (list a k r))

(with-test-prefix "srfi-89"

  (pass-if "default-values-1" (equal? (f 1) '(1 #f)))
  (pass-if "default-values-2" (equal? (f 1 2) '(1 2)))
  (pass-if "argument-count-1" (not (false-if-exception (f 1 2 3))))

  (pass-if "default-values-3" (equal? (g 3) '(3 3 9)))
  (pass-if "default-values-4" (equal? (g 3 4) '(3 4 12)))
  (pass-if "malformed-keyword" (not (false-if-exception (g 3 4 key:))))
  (pass-if "keyword-1" (equal? (g 3 4 key: 5) '(3 4 5)))
  (pass-if "undefined-keyword" (not (false-if-exception (g 3 4 zoo: 5))))
  (pass-if "duplicate-keyword" 
    (not (false-if-exception (g 3 4 key: 5 key: 6))))

  (pass-if "keywords-and-rest-1" (equal? (h1 7) '(7 #f ())))
  (pass-if "keywords-and-rest-2" (equal? (h1 7 8 9 10) '(7 #f (8 9 10))))
  (pass-if "keywords-and-rest-3" (equal? (h1 7 key: 8 9 10) '(7 8 (9 10))))
  (pass-if "undefined-keyword-2" 
    (not (false-if-exception (h1 7 key: 8 zoo: 9))))
  
  (pass-if "required-positionals-1" (equal? (h2 7) '(7 #f ())))
  (pass-if "required-positionals-2" (equal? (h2 7 8 9 10) '(7 #f (8 9 10))))
  (pass-if "required-positionals-3" (equal? (h2 key: 8 9 10) '(9 8 (10))))
  (pass-if "undefined-keyword-3" 
    (not (false-if-exception (h2 key: 8 zoo: 9) ))))

;;; Local Variables:
;;; coding: latin-1
;;; End:

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

end of thread, other threads:[~2008-08-22  4:12 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-05-03  3:37 pass at srfi-89 implementation Julian Graham
2008-05-05 21:43 ` Ludovic Courtès
2008-05-19 20:15 ` Ludovic Courtès
2008-05-19 20:28   ` Julian Graham
2008-05-20  9:04     ` Ludovic Courtès
2008-05-25  5:08       ` Julian Graham
2008-05-27  7:43         ` Ludovic Courtès
2008-07-28  4:19           ` Julian Graham
2008-08-11 11:48             ` Ludovic Courtès
2008-08-16 21:19               ` Julian Graham
2008-08-18 18:41                 ` Andy Wingo
2008-08-20  7:32                   ` Ludovic Courtès
2008-08-20 20:14                     ` Andy Wingo
2008-08-22  4:12                       ` Julian Graham
2008-08-22  3:56                 ` Julian Graham

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).