* 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
* Re: pass at srfi-89 implementation 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 1 sibling, 0 replies; 15+ messages in thread From: Ludovic Courtès @ 2008-05-05 21:43 UTC (permalink / raw) To: guile-devel Hi Julian, "Julian Graham" <joolean@gmail.com> writes: > So I've taken a stab at implementing SRFI-89 This is good news! Neil and I have agreed to release 1.8.5 ASAP, which I'll try to do by the end of the week, after which I'll look into your SRFI-89 module if no one beats me at it. ;-) Thanks, Ludovic. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: pass at srfi-89 implementation 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 1 sibling, 1 reply; 15+ messages in thread From: Ludovic Courtès @ 2008-05-19 20:15 UTC (permalink / raw) To: guile-devel Hi Julian, "Julian Graham" <joolean@gmail.com> writes: > 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: Would it be a lot more time to make SRFI-89 independent of `(ice-9 optargs)' given the differences you describe? IMO it would look cleaner and be more robust in the face of changes in `(ice-9 optargs)'. It would also make sure the SRFI-89 implementation is not subtly biased towards what's in `(ice-9 optargs)'. What do you think? Ludovic. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: pass at srfi-89 implementation 2008-05-19 20:15 ` Ludovic Courtès @ 2008-05-19 20:28 ` Julian Graham 2008-05-20 9:04 ` Ludovic Courtès 0 siblings, 1 reply; 15+ messages in thread From: Julian Graham @ 2008-05-19 20:28 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-devel Well, there's a reference implementation of SRFI-89 that (I think) could just be dropped in and used, now that we have SRFI-88-style keywords. I was just hoping that the existence of `(ice-9 optargs)' would make possible a more compact / potentially more efficient implementation. > Would it be a lot more time to make SRFI-89 independent of `(ice-9 > optargs)' given the differences you describe? > > IMO it would look cleaner and be more robust in the face of changes in > `(ice-9 optargs)'. It would also make sure the SRFI-89 implementation > is not subtly biased towards what's in `(ice-9 optargs)'. > > What do you think? ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: pass at srfi-89 implementation 2008-05-19 20:28 ` Julian Graham @ 2008-05-20 9:04 ` Ludovic Courtès 2008-05-25 5:08 ` Julian Graham 0 siblings, 1 reply; 15+ messages in thread From: Ludovic Courtès @ 2008-05-20 9:04 UTC (permalink / raw) To: guile-devel Hi, "Julian Graham" <joolean@gmail.com> writes: > Well, there's a reference implementation of SRFI-89 that (I think) > could just be dropped in and used, now that we have SRFI-88-style > keywords. It looks like the license terms would indeed allow us to do that. I'm not sure whether there's a precedent including non-LGPL SRFI code as is, though. Besides, we'd have to check whether the reference implementation, which targets Gambit's compiler, is suitable for Guile. Thoughts? > I was just hoping that the existence of `(ice-9 optargs)' > would make possible a more compact / potentially more efficient > implementation. Yes, it also stroke me as a worthy goal at first, but the differences you listed led me to think we might be better off with a separate implementation. Thanks, and sorry for the hassle! Ludovic. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: pass at srfi-89 implementation 2008-05-20 9:04 ` Ludovic Courtès @ 2008-05-25 5:08 ` Julian Graham 2008-05-27 7:43 ` Ludovic Courtès 0 siblings, 1 reply; 15+ messages in thread From: Julian Graham @ 2008-05-25 5:08 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-devel > It looks like the license terms would indeed allow us to do that. I'm > not sure whether there's a precedent including non-LGPL SRFI code as is, > though. Well, it wouldn't have to be 100% as-is -- aside from adding the module declaration, there are some things I wouldn't mind tweaking a bit, such as where the argument-parsing helper functions get declared / exported. > Besides, we'd have to check whether the reference implementation, which > targets Gambit's compiler, is suitable for Guile. Having done a very quick and dirty integration, I can say that it passes the rudimentary test suite I pulled from the SRFI document. In terms of whether it's suitable... well, Marc claims his implementation isn't even suitable for Gambit, which apparently has a native implementation: "To give a rough idea of the speed improvement, a trivial procedure with 10 optional named parameters and called with 5 named parameters runs 14 times faster and generates no garbage when the Gambit compiler's builtin optional parameter passing mechanism is used." I haven't benchmarked it against `(ice-9 optargs)', but it seems like our options are: Use the reference implementation, optimizing it as best we can for the peculiarities of Guile; write our own implementation in Scheme or C that would exist in parallel with `(ice-9 optargs)'; or enhance `(ice-9 optargs)' to allow a dependent implementation such as the one I submitted to work correctly. What do you think? Regards, Julian ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: pass at srfi-89 implementation 2008-05-25 5:08 ` Julian Graham @ 2008-05-27 7:43 ` Ludovic Courtès 2008-07-28 4:19 ` Julian Graham 0 siblings, 1 reply; 15+ messages in thread From: Ludovic Courtès @ 2008-05-27 7:43 UTC (permalink / raw) To: guile-devel Hi, "Julian Graham" <joolean@gmail.com> writes: > Having done a very quick and dirty integration, I can say that it > passes the rudimentary test suite I pulled from the SRFI document. In > terms of whether it's suitable... well, Marc claims his implementation > isn't even suitable for Gambit, which apparently has a native > implementation: > > "To give a rough idea of the speed improvement, a trivial procedure > with 10 optional named parameters and called with 5 named parameters > runs 14 times faster and generates no garbage when the Gambit > compiler's builtin optional parameter passing mechanism is used." Ouch! > I haven't benchmarked it against `(ice-9 optargs)', but it seems like > our options are: Use the reference implementation, optimizing it as > best we can for the peculiarities of Guile; write our own > implementation in Scheme or C that would exist in parallel with > `(ice-9 optargs)'; or enhance `(ice-9 optargs)' to allow a dependent > implementation such as the one I submitted to work correctly. > > What do you think? I'm still not convinced by the last option. Given the above, Option 2 (that is, writing our own, preferably in Scheme) seems to be the safest. Hopefully this isn't too much work, but the above quote indicates that we should be careful about performance. ;-) Would you like to try doing it? Thanks, Ludovic. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: pass at srfi-89 implementation 2008-05-27 7:43 ` Ludovic Courtès @ 2008-07-28 4:19 ` Julian Graham 2008-08-11 11:48 ` Ludovic Courtès 0 siblings, 1 reply; 15+ messages in thread From: Julian Graham @ 2008-07-28 4:19 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-devel > I'm still not convinced by the last option. Given the above, Option 2 > (that is, writing our own, preferably in Scheme) seems to be the safest. > Hopefully this isn't too much work, but the above quote indicates that > we should be careful about performance. ;-) > > Would you like to try doing it? [Time passes.] Okay, I've tried -- at length. And so far I haven't been able to top the performance of the reference implementation. In fact, it actually seems to be fairly efficient (I now think Marc's quote above about Gambit's built-in support says less about the relative shortcomings of his particular implementation and more about the benefits of doing something like this in native code). From what I can see, there are only a few areas in which the reference implementation could be improved by Guile-specific features: * Native hash tables (Marc's code has its own hash table implementation) * Use of `hashq-get-handle' (the reference implementation relies on the identity of an `undefined' object) * Symbol generation for naming run-time helper functions (the reference implementation explicitly exports bindings for helper functions) So it looks like the right way to go might be to tweak Marc's implementation so that it takes advantage of the above, although I don't see it improving the performance all that much. Alternatively, we could do this in C, but I don't know if it's worth the added complexity. Thoughts? Regards, Julian ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: pass at srfi-89 implementation 2008-07-28 4:19 ` Julian Graham @ 2008-08-11 11:48 ` Ludovic Courtès 2008-08-16 21:19 ` Julian Graham 0 siblings, 1 reply; 15+ messages in thread From: Ludovic Courtès @ 2008-08-11 11:48 UTC (permalink / raw) To: guile-devel Hi Julian, "Julian Graham" <joolean@gmail.com> writes: > Okay, I've tried -- at length. And so far I haven't been able to top > the performance of the reference implementation. In fact, it actually > seems to be fairly efficient Did you run some sort of "benchmark"? > From what I can see, there are > only a few areas in which the reference implementation could be > improved by Guile-specific features: > > * Native hash tables (Marc's code has its own hash table implementation) > * Use of `hashq-get-handle' (the reference implementation relies on > the identity of an `undefined' object) The reference implementation strives to create a perfect hash table, while Guile's built-in hash tables may not be perfect all the time (there can be collisions, although tables are automatically resized once in a while). So, theoretically, the reference implementation's solution is more efficient. OTOH, as is often the case with Guile, it may turn out to be faster to use Guile's built-in hash tables, especially since they may end up being "perfect" (i.e., collision-free) in most cases here; in addition, running one `make-perfect-hash-table' for each `lambda*' at initialization time may turn out to be more costly than just `make-hash-table'. Could you try that out? > * Symbol generation for naming run-time helper functions (the > reference implementation explicitly exports bindings for helper > functions) Hmm, what are you referring to precisely? Also: * use Guile's `string-hash' in `$hash-keyword' (unless Guile's hash tables are used, that is). Overall, it may indeed be wise to use the reference implementation. However, we'd need a copyright assignment from Marc and/or licensing under LGPLv2+. Thanks, Ludovic. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: pass at srfi-89 implementation 2008-08-11 11:48 ` Ludovic Courtès @ 2008-08-16 21:19 ` Julian Graham 2008-08-18 18:41 ` Andy Wingo 2008-08-22 3:56 ` Julian Graham 0 siblings, 2 replies; 15+ messages in thread From: Julian Graham @ 2008-08-16 21:19 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-devel (Sorry for the late reply -- I was out of the country last week...) >> Okay, I've tried -- at length. And so far I haven't been able to top >> the performance of the reference implementation. In fact, it actually >> seems to be fairly efficient > > Did you run some sort of "benchmark"? Yes. Nothing formal, though -- I pretty much just defined a number of different SRFI-89-style functions with different signatures (optional arguments, keyword arguments, optional and keyword arguments) and used `(gettimeofday)' to see how long it took to run them in a loop a few hundred thousand times. On average, my implementation still takes about 70% longer than Marc's, which is pretty embarrassing, but I'm still attempting to profile and optimize it (at the moment it looks like most of the time is spent in an initial sorting pass). (Has anyone else noticed that statprof hasn't been working for a while now? I get a "Numerical overflow" from `statprof-display' no matter what arguments I pass to `statprof-reset'.) > The reference implementation strives to create a perfect hash table, > while Guile's built-in hash tables may not be perfect all the time > (there can be collisions, although tables are automatically resized once > in a while). So, theoretically, the reference implementation's solution > is more efficient. > > OTOH, as is often the case with Guile, it may turn out to be faster to > use Guile's built-in hash tables, especially since they may end up being > "perfect" (i.e., collision-free) in most cases here; in addition, > running one `make-perfect-hash-table' for each `lambda*' at > initialization time may turn out to be more costly than just > `make-hash-table'. Could you try that out? Yeah, I'm already using Guile's hash tables and creating the table with a big enough initial size to store all of the keywords specified in the argument list -- and since Guile's hash tables are native, my (naive) assumption is that they're probably as fast or faster than a pure-Scheme perfect hashing system. But I could try perfect hashing. >> * Symbol generation for naming run-time helper functions (the >> reference implementation explicitly exports bindings for helper >> functions) > > Hmm, what are you referring to precisely? Well, like Marc's version, my code requires a few named procedures at runtime to handle things like argument processing (see `$process-args' in the reference implementation) buts needs to avoid collisions between these bindings and bindings in user code. I'm assuming this was Marc's intent in naming his helper procedures with $-signs and binding them in the top-level environment -- i.e. that he made it obvious which names users need to avoid. But Guile's `gensym' allows for the creation of local (to the lambda* expression) bindings that are guaranteed not to shadow anything in the user's environment. > Overall, it may indeed be wise to use the reference implementation. > However, we'd need a copyright assignment from Marc and/or licensing > under LGPLv2+. Yeah. My approach thus far has been to do this as a "clean room" implementation based only on the SRFI-89 spec, and it goes without saying that Marc understands this SRFI more intimately than I do. Maybe let me hack on it a bit longer and report back, though? Regards, Julian ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: pass at srfi-89 implementation 2008-08-16 21:19 ` Julian Graham @ 2008-08-18 18:41 ` Andy Wingo 2008-08-20 7:32 ` Ludovic Courtès 2008-08-22 3:56 ` Julian Graham 1 sibling, 1 reply; 15+ messages in thread From: Andy Wingo @ 2008-08-18 18:41 UTC (permalink / raw) To: Julian Graham; +Cc: Ludovic Courtès, guile-devel Hi Julian, On Sat 16 Aug 2008 14:19, "Julian Graham" <joolean@gmail.com> writes: > (Has anyone else noticed that statprof hasn't been working for a while > now? I get a "Numerical overflow" from `statprof-display' no matter > what arguments I pass to `statprof-reset'.) I haven't noticed this, no. Normally this would mean that we're dividing by zero, perhaps no counts were taken or something. (Perhaps we should pull statprof into guile itself.) Let me know if you get around to fixing this before I do. Andy -- http://wingolog.org/ ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: pass at srfi-89 implementation 2008-08-18 18:41 ` Andy Wingo @ 2008-08-20 7:32 ` Ludovic Courtès 2008-08-20 20:14 ` Andy Wingo 0 siblings, 1 reply; 15+ messages in thread From: Ludovic Courtès @ 2008-08-20 7:32 UTC (permalink / raw) To: guile-devel Hi, Andy Wingo <wingo@pobox.com> writes: > I haven't noticed this, no. Normally this would mean that we're dividing > by zero, perhaps no counts were taken or something. (Perhaps we should > pull statprof into guile itself.) Let me know if you get around to > fixing this before I do. It could be that your program didn't spend enough time in user space, too. Speaking of which: Andy, did you see this discussion? http://thread.gmane.org/gmane.lisp.guile.bugs/3895 I'm concerned about the accuracy of `statprof' given that handlers are called asynchronously. Thanks, Ludovic. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: pass at srfi-89 implementation 2008-08-20 7:32 ` Ludovic Courtès @ 2008-08-20 20:14 ` Andy Wingo 2008-08-22 4:12 ` Julian Graham 0 siblings, 1 reply; 15+ messages in thread From: Andy Wingo @ 2008-08-20 20:14 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-devel Hi, On Wed 20 Aug 2008 00:32, ludo@gnu.org (Ludovic Courtès) writes: > http://thread.gmane.org/gmane.lisp.guile.bugs/3895 > > I'm concerned about the accuracy of `statprof' given that handlers are > called asynchronously. Signal handlers have always been called asynchronously, so there's been some fuzziness as to when statprof handlers get called; in practice however it's corresponded pretty well. I just ran the statprof unit test and it seems to still be OK. Perhaps in Julien's run, there simply was not enough time in user space relative to the sampling frequency. (Certainly statprof should deal with this in a more intelligent fashion.) Andy -- http://wingolog.org/ ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: pass at srfi-89 implementation 2008-08-20 20:14 ` Andy Wingo @ 2008-08-22 4:12 ` Julian Graham 0 siblings, 0 replies; 15+ messages in thread From: Julian Graham @ 2008-08-22 4:12 UTC (permalink / raw) To: Andy Wingo; +Cc: Ludovic Courtès, guile-devel > I just ran the statprof unit test and it seems to still be OK. Perhaps > in Julien's run, there simply was not enough time in user space relative > to the sampling frequency. (Certainly statprof should deal with this in > a more intelligent fashion.) That's certainly possible (I take it you're using "user space" to mean "non-primitive function code"), although I know it's collecting some samples at least because I've added `display' calls to statprof itself. One thing I just noticed is that the whole profiling process seems to work fine if count-calls? is false -- is that part of the unit test? On a more general note, it would be great if statprof were more robust / could profile more and different types of code. When you say "pull statprof into guile itself," Andy, do you mean getting profiling functionality into libguile or just including the module in the core distribution? (The former would be pretty sweet...) Regards, Julian ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: pass at srfi-89 implementation 2008-08-16 21:19 ` Julian Graham 2008-08-18 18:41 ` Andy Wingo @ 2008-08-22 3:56 ` Julian Graham 1 sibling, 0 replies; 15+ messages in thread From: Julian Graham @ 2008-08-22 3:56 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-devel [-- Attachment #1: Type: text/plain, Size: 911 bytes --] Alright, I give up. I'm still not exactly sure why my implementation is as slow as it is; I have a hunch that I'm taking too long processing the list of actual parameters, but I haven't been able to glean many specifics from statprof. Marc also employs some nifty but un-Scheme-y (to my mind, at least) tricks that give his version an edge, like emitting code that destructively modifies the argument list as part of determining where to insert default values. At any rate, find my version attached. I think it's probably a dead end in terms of going forward, but maybe it's salvageable by a more experienced Schemer? ...Or maybe a more experienced Schemer could make another attempt at doing an implementation from scratch? I don't know what the right course is -- I think it would probably be easy to do something performant in C, but I also grok why that's not the preferred solution. Regards, Julian [-- Attachment #2: srfi-89.scm --] [-- Type: application/octet-stream, Size: 6945 bytes --] (define-module (srfi srfi-89) #:use-module (ice-9 receive) #:use-module (srfi srfi-1) #:use-module (srfi srfi-88) #:export ( define* lambda*)) (cond-expand-provide (current-module) '(srfi-89)) (define-macro (define* pattern . body) (if (pair? pattern) `(define ,(car pattern) (lambda* ,(cdr pattern) ,@body)) `(define ,pattern ,@body))) (define variable? symbol?) (define required? variable?) (define (optional? x) (and (proper-list? x) (= (length x) 2) (variable? (car x)))) (define (positional? x) (or (required? x) (optional? x))) (define (named? x) (and (proper-list? x) (= (length x) 3) (keyword? (car x)) (variable? (cadr x)))) (define-macro (lambda* formals . body) ;; Because Guile's SRFI-1 doesn't work on dotted lists (define (length-permissive lst) (if (null? lst) 0 (let ((clst (cdr lst))) (if (pair? clst) (+ (length-permissive clst) 1) 2)))) (define (span-permissive pred lst) (define (span-permissive-inner head tail) (if (or (null? tail) (not (pair? tail))) (values head tail) (let ((ctail (car tail))) (if (pred ctail) (span-permissive-inner (append head (list ctail)) (cdr tail)) (values head tail))))) (span-permissive-inner '() lst)) (define (emit required optional named rest named-first?) (define (emit-positional reqv optv loptv nmdv rstv) (define (emit-positional-tail) (if (or named-first? (null? named)) (emit-rest rstv) (emit-named reqv optv loptv nmdv rstv))) (append (fold (lambda (x c) (append c `((,x (vector-ref ,reqv ,(length c)))))) '() required) (if (null? optional) (emit-positional-tail) (let ((len (gensym)) (p (gensym))) (append (fold (lambda (x c) (append c `((,x (if (> ,loptv ,(length c)) (vector-ref ,optv ,(length c)) ,(list-ref (map cadr optional) (length c))))))) '() (map car optional)) (emit-positional-tail)))))) (define (emit-named reqv optv loptv nmdv rstv) (define (emit-named-tail) (if named-first? (emit-positional reqv optv loptv nmdv rstv) (emit-rest rstv))) (if (null? named) (emit-named-tail) (let ((handle-var (gensym))) (append (fold (lambda (x c) (append c `((,(cadr x) (let ((,handle-var (hashq-get-handle ,nmdv ,(car x)))) (if ,handle-var (cdr ,handle-var) ,(caddr x))))))) '() named) (emit-named-tail))))) (define (emit-rest rstv) (if rest `((,rest ,rstv)) '())) (let* ((srfi-89:bound-required-var (gensym)) (srfi-89:bound-optional-var (gensym)) (srfi-89:bound-required-counter (gensym)) (srfi-89:bound-optional-counter (gensym)) (srfi-89:bound-named-var (gensym)) (srfi-89:bound-rest-var (gensym)) (srfi-89:process-args (gensym)) (srfi-89:num-required (length required)) (srfi-89:num-optional (length optional))) `(lambda srfi-89:args (let ,srfi-89:process-args ((,srfi-89:bound-required-var ,(if (null? required) #f `(make-vector ,srfi-89:num-required))) (,srfi-89:bound-optional-var ,(if (null? required) #f `(make-vector ,srfi-89:num-required))) (,srfi-89:bound-required-counter 0) (,srfi-89:bound-optional-counter 0) (,srfi-89:bound-named-var ,(if (null? named) #f `(make-hash-table))) (,srfi-89:bound-rest-var '()) (lst srfi-89:args)) (if (null? lst) (begin (let* ,(if named-first? (emit-named srfi-89:bound-required-var srfi-89:bound-optional-var srfi-89:bound-optional-counter srfi-89:bound-named-var srfi-89:bound-rest-var) (emit-positional srfi-89:bound-required-var srfi-89:bound-optional-var srfi-89:bound-optional-counter srfi-89:bound-named-var srfi-89:bound-rest-var)) ,@body)) (let ((cl (car lst))) (cond ,(if (not (null? named)) `((keyword? cl) (if (not (memq cl (quote ,(map car named)))) (error "unknown parameter keyword" cl)) (if (hashq-get-handle ,srfi-89:bound-named-var cl) (error "duplicate parameter" cl)) (hashq-set! ,srfi-89:bound-named-var cl (cadr lst)) (,srfi-89:process-args ,srfi-89:bound-required-var ,srfi-89:bound-optional-var ,srfi-89:bound-required-counter ,srfi-89:bound-optional-counter ,srfi-89:bound-named-var ,srfi-89:bound-rest-var (cddr lst))) `(#f)) ((< ,srfi-89:bound-required-counter ,srfi-89:num-required) (vector-set! ,srfi-89:bound-required-var ,srfi-89:bound-required-counter cl) (,srfi-89:process-args ,srfi-89:bound-required-var ,srfi-89:bound-optional-var (+ ,srfi-89:bound-required-counter 1) ,srfi-89:bound-optional-counter ,srfi-89:bound-named-var ,srfi-89:bound-rest-var (cdr lst))) ((< ,srfi-89:bound-optional-counter ,srfi-89:num-optional) (vector-set! ,srfi-89:bound-optional-var ,srfi-89:bound-optional-counter cl) (,srfi-89:process-args ,srfi-89:bound-required-var ,srfi-89:bound-optional-var ,srfi-89:bound-required-counter (+ ,srfi-89:bound-optional-counter 1) ,srfi-89:bound-named-var ,srfi-89:bound-rest-var (cdr lst))) ((quote ,rest) (,srfi-89:process-args ,srfi-89:bound-required-var ,srfi-89:bound-optional-var ,srfi-89:bound-required-counter ,srfi-89:bound-optional-counter ,srfi-89:bound-named-var lst '())) (else (error "too many actual parameters"))))))))) (define (parse-1 positional named rest named-first?) (receive (required optional) (span-permissive required? positional) (emit required optional named rest named-first?))) (cond ((null? formals) `(lambda () ,@body)) ((variable? formals) `(lambda ,formals ,@body)) ((positional? (car formals)) (receive (positional named) (span-permissive positional? formals) (cond ((and (not (symbol? named)) (dotted-list? named)) (receive (named rest) (split-at named (- (length-permissive named) 1)) (parse-1 positional named rest #f))) ((list? named) (parse-1 positional named #f #f)) (else (parse-1 positional '() named #f))))) ((named? (car formals)) (receive (named positional) (span-permissive named? formals) (cond ((and (not (symbol? positional)) (dotted-list? positional)) (receive (positional rest) (split-at positional (- (length-permissive positional) 1)) (parse-1 positional named rest #t))) ((list? positional) (parse-1 positional named #f #t)) (else (parse-1 '() named positional #t))))) (else (error "Error in formal parameter list")))) ^ 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).