* and-let* is not composable? @ 2013-09-09 17:35 Panicz Maciej Godek 2013-09-09 20:26 ` Stefan Israelsson Tampe 2013-09-10 17:57 ` Ian Price 0 siblings, 2 replies; 14+ messages in thread From: Panicz Maciej Godek @ 2013-09-09 17:35 UTC (permalink / raw) To: guile-user@gnu.org [-- Attachment #1: Type: text/plain, Size: 3025 bytes --] Hi, some time ago I posted to comp.lang.scheme with the following proposal of "define-curried" macro: (define-macro (define-curried signature . body) (match signature ((name args ...) `(define-syntax ,name (syntax-rules () ((_ ,@args) (begin ,@body)) ,@(let loop ((args* args)) (match args* (() '()) ((first ... last) (cons `((_ ,@first #;...) (lambda(,last)(,name ,@args*))) (loop first #;...)))))))))) The idea was to expand, e.g. (define-curried (f a b c d) (list a b c d)) to: (define-syntax f (syntax-rules () ((_ a b c d) (begin (list a b c d))) ((_ a b c) (lambda(d) (f a b c d))) ((_ a b) (lambda(c) (f a b c))) ((_ a) (lambda(b) (f a b))) ((_) (lambda(a) (f a))))) I asked whether it would be possible to write that code using syntax-rules only, but I received no answer, not even a reprimend. I used that code to implement a quite convinient macro (actually that urge was my inspiration): (define-curried (matches? pattern x) (match x (pattern #t) (else #f))) so that I could write (filter (matches? (two elements)) some-list) Recently, I tried to write a nicer interface to string-match, that would allow me to extract parenthesized subexpressions easily. My first guess was this: (define-curried (string-matches pattern string) ;;CAUTION: buggy version (and-let* ((match-struct (string-match pattern string)) (count (match:count match-struct))) (map (lambda(n)(match:substring match-struct n)) (iota (1- count) 1)))) and although it worked with a complete list of arguments, (string-matches "([a-z])" "a") ==> ("a") it failed to curry properly ((string-matches "([a-z])") "a") ==> some strange error It turned out, that the "string" symbol doesn't get tied with the lambda argument: (expand (string-matches "([a-z])")) ==> (lambda (string-12552) (let ((match-struct-12557 (string-match "([a-z])" string))) ;; the reason of our tears and despair is right here^^^ (if match-struct-12557 (let ((count-12561 (match:count match-struct-12557))) (if count-12561 (map (lambda (n-12564) (match:substring match-struct-12557 n-12564)) (iota (#{1-}# count-12561) 1)) #f)) #f))) This forced me to write another definition of string-matches that doesn't use the and-let* macro and works as expected: (define-curried (string-matches pattern s) (let ((match-struct (string-match pattern s))) (if match-struct (let ((count (match:count match-struct))) (map (lambda(n)(match:substring match-struct n)) (iota (1- count) 1))) #f))) Nevertheless I am a little worried that either my macro, or and-let* is not composable. Perhaps there's some wise man here who knows what's going on. Best regards, M. [-- Attachment #2: Type: text/html, Size: 8543 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: and-let* is not composable? 2013-09-09 17:35 and-let* is not composable? Panicz Maciej Godek @ 2013-09-09 20:26 ` Stefan Israelsson Tampe 2013-09-09 21:34 ` Ian Price 2013-09-10 17:57 ` Ian Price 1 sibling, 1 reply; 14+ messages in thread From: Stefan Israelsson Tampe @ 2013-09-09 20:26 UTC (permalink / raw) To: guile-user, guile-devel First of all define-macro is asking for trouble. don't use it is the general recomendation for guile. If you look into the kanren soures you will find, (define-syntax lambda@ (syntax-rules () ((_ (formal) body0 body1 ...) (lambda (formal) body0 body1 ...)) ((_ (formal0 formal1 formal2 ...) body0 body1 ...) (lambda (formal0) (lambda@ (formal1 formal2 ...) body0 body1 ...))))) (define-syntax @ (syntax-rules () ((_ rator rand) (rator rand)) ((_ rator rand0 rand1 rand2 ...) (@ (rator rand0) rand1 rand2 ...)))) That is currying where lambda@ is your define-curried and @ is the application of curried functions in a convinient way e.g. (test-check 'test-@-lambda@ (@ (lambda@ (x y z) (+ x (+ y z))) 1 2 3) 6) It's not what you wnt but this gives you a good pattern to maybe base your work on. If you still wan't to design defined curried ontop of syntax-rules I would recoment to use the ck macro e.g. (define-syntax ck (syntax-rules (quote) ((ck () 'v) v) ; yield the value on empty stack ((ck (((op ...) ea ...) . s) 'v) ; re-focus on the other argument, ea (ck-arg s (op ... 'v) ea ...)) ((ck s (op ea ...)) ; Focus: handling an application; (ck-arg s (op) ea ...)))) ; check if args are values (define-syntax ck-arg (syntax-rules (quote) ((ck-arg s (op va ...)) ; all arguments are evaluated, (op s va ...)) ; do the redex ((ck-arg s (op ...) 'v ea1 ...) ; optimization when the first ea (ck-arg s (op ... 'v) ea1 ...)) ; was already a value ((ck-arg s (op ...) ea ea1 ...) ; focus on ea, to evaluate it (ck (((op ...) ea1 ...) . s) ea)))) 1) define a partitioner, (a b c) -> (() a) ((a) b) ((a b) c)) with (define-syntax ck-partition (syntax-rules (quote) ((_ s '(a ... b) 'l) (ck-partition s '(a ...) '(((a ...) b) . l))) ((_ s '() 'l) (ck s 'l)))) 2) compile the pieces together (define-syntax compile-curried (syntax-rules (quote) ((_ s 'name '(a ...) 'body '(((b ...) c) ...)) (ck () '(define-syntax name (syntax-rules () ((_ a ...) (begin . body)) ((_ b ...) (lambda (c) (name b ... c))) ...)))))) 3) The overall macro (define-syntax-rule (define-curried (name a ...) . body) (ck () (compile-curried 'name '(a ...) 'body (ck-partition '(a ...) '())))) Now we need the definition of the ck macro, http://okmij.org/ftp/Scheme/macros.html We have it in guile also in master. So lets try it, ... scheme@(guile-user)> (define-curried (f a b c d) (list a b c d)) scheme@(guile-user)> (((f 1 2) 3) 4) $5 = (1 2 3 4) it works, lets try out the problematic vode you have, (use-modules (srfi srfi-2)) (use-modules (srfi srfi-1)) (define-curried (string-matches pattern string) (and-let* ((match-struct (string-match pattern string)) (count (match:count match-struct))) (map (lambda(n)(match:substring match-struct n)) (iota (1- count) 1)))) scheme@(guile-user)> (string-matches "([a-z])" "a") $4 = ("a") ,exp ((string-matches "([a-z])") "a") $5 = ((lambda (string-871) (let ((match-struct-876 (string-match "([a-z])" string))) (if match-struct-876 (let ((count-880 (match:count match-struct-876))) (if count-880 (map (lambda (n-883) (match:substring match-struct-876 n-883)) (iota (#{1-}# count-880) 1)) #f)) #f))) "a") And we see that string is not managed correctly. Is this a bug? I can't understand why this is not treated as intended! > On Monday, September 09, 2013 07:35:16 PM Panicz Maciej Godek wrote: > Hi, > some time ago I posted to comp.lang.scheme with the > following proposal of "define-curried" macro: > > (define-macro (define-curried signature . body) > (match signature > ((name args ...) > `(define-syntax ,name > (syntax-rules () > ((_ ,@args) > (begin ,@body)) > ,@(let loop ((args* args)) > (match args* > (() '()) > ((first ... last) > (cons `((_ ,@first #;...) > (lambda(,last)(,name ,@args*))) > (loop first #;...)))))))))) > > The idea was to expand, e.g. (define-curried (f a b c d) (list a b c > d)) to: > > (define-syntax f > (syntax-rules () > ((_ a b c d) > (begin (list a b c d))) > ((_ a b c) > (lambda(d) > (f a b c d))) > ((_ a b) > (lambda(c) > (f a b c))) > ((_ a) > (lambda(b) > (f a b))) > ((_) > (lambda(a) > (f a))))) > > I asked whether it would be possible to write that code using > syntax-rules only, but I received no answer, not even a reprimend. I > used that code to implement a quite convinient macro (actually that > urge was my inspiration): > > (define-curried (matches? pattern x) > (match x > (pattern #t) > (else #f))) > > so that I could write > > (filter (matches? (two elements)) some-list) > > Recently, I tried to write a nicer interface to string-match, that > would allow me to extract parenthesized subexpressions easily. My > first guess was this: > > (define-curried (string-matches pattern string) > ;;CAUTION: buggy version > (and-let* ((match-struct (string-match pattern string)) > (count (match:count match-struct))) > (map (lambda(n)(match:substring match-struct n)) > (iota (1- count) 1)))) > > and although it worked with a complete list of arguments, > (string-matches "([a-z])" "a") > ==> ("a") > it failed to curry properly > ((string-matches "([a-z])") "a") > ==> some strange error > > It turned out, that the "string" symbol doesn't get tied > with the lambda argument: > > (expand (string-matches "([a-z])")) > ==> > (lambda (string-12552) > (let ((match-struct-12557 (string-match "([a-z])" string))) > ;; the reason of our tears and despair is right here^^^ > (if match-struct-12557 > (let ((count-12561 (match:count match-struct-12557))) > (if count-12561 > (map (lambda (n-12564) > (match:substring match-struct-12557 n-12564)) > (iota (#{1-}# count-12561) 1)) > #f)) > #f))) > > This forced me to write another definition of string-matches > that doesn't use the and-let* macro and works as expected: > > (define-curried (string-matches pattern s) > (let ((match-struct (string-match pattern s))) > (if match-struct > (let ((count (match:count match-struct))) > (map (lambda(n)(match:substring match-struct n)) > (iota (1- count) 1))) > #f))) > > Nevertheless I am a little worried that either my macro, > or and-let* is not composable. Perhaps there's some wise > man here who knows what's going on. > > Best regards, > M. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: and-let* is not composable? 2013-09-09 20:26 ` Stefan Israelsson Tampe @ 2013-09-09 21:34 ` Ian Price 2013-09-10 13:42 ` Stefan Israelsson Tampe 2013-11-02 2:39 ` Ian Price 0 siblings, 2 replies; 14+ messages in thread From: Ian Price @ 2013-09-09 21:34 UTC (permalink / raw) To: Stefan Israelsson Tampe; +Cc: guile-user, guile-devel Stefan Israelsson Tampe <stefan.itampe@gmail.com> writes: > (use-modules (srfi srfi-2)) > (use-modules (srfi srfi-1)) > > (define-curried (string-matches pattern string) > (and-let* ((match-struct (string-match pattern string)) > (count (match:count match-struct))) > (map (lambda(n)(match:substring match-struct n)) > (iota (1- count) 1)))) > > scheme@(guile-user)> (string-matches "([a-z])" "a") > $4 = ("a") > > > ,exp ((string-matches "([a-z])") "a") > $5 = ((lambda (string-871) > (let ((match-struct-876 > (string-match "([a-z])" string))) > (if match-struct-876 > (let ((count-880 (match:count match-struct-876))) > (if count-880 > (map (lambda (n-883) > (match:substring match-struct-876 n-883)) > (iota (#{1-}# count-880) 1)) > #f)) > #f))) > "a") > > And we see that string is not managed correctly. Is this a bug? I > can't understand why this is not treated as intended! The problem is one that occurs when hygienic and non-hygienic macros are mixed. Here, and-let* is the non-hygienic one. Basically, in a hygienic macro, you carry around a bunch of context to allow you to refer to the right variable names. Then defmacro comes along, strips all that away, and uses the raw symbol names. We can fix and-let* to be hygienic, that's pretty easy, but I'm not sure what you can do about this in general. It's not like you can pass in the gensymed name, because that will break the way people who for some reason still right defmacros expect them to work. Dunno, maybe I'm just missing some insight here. -- Ian Price -- shift-reset.com "Programming is like pinball. The reward for doing it well is the opportunity to do it again" - from "The Wizardy Compiled" ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: and-let* is not composable? 2013-09-09 21:34 ` Ian Price @ 2013-09-10 13:42 ` Stefan Israelsson Tampe 2013-09-10 13:51 ` Ian Price 2013-11-02 2:39 ` Ian Price 1 sibling, 1 reply; 14+ messages in thread From: Stefan Israelsson Tampe @ 2013-09-10 13:42 UTC (permalink / raw) To: Stefan Israelsson Tampe, guile-user, guile-devel [-- Attachment #1: Type: text/plain, Size: 3372 bytes --] Ahh, I thought we nuked the unhygienic macros in our tool-chain, That is a BUG! Anyway can I suggest a Bug report where we 1) Make the unhygienic define-macros in the following files hygienic, ------------------------------------------------- module/srfi/srfi-4.scm: module/srfi/srfi-69.scm: module/srfi/srfi-4/gnu.scm: module/system/base/syntax.scm: module/system/base/language.scm: module/system/base/lalr.upstream.scm: module/statprof.scm: module/oop/goops.scm: module/oop/goops/save.scm: module/oop/goops/accessors.scm: module/texinfo/plain-text.scm: module/ice-9/i18n.scm: module/ice-9/time.scm: module/ice-9/serialize.scm: module/ice-9/session.scm: module/ice-9/optargs.scm: module/scripts/snarf-check-and-output-texi.scm: module/scripts/lint.scm: module/sxml/ssax/input-parse.scm: module/sxml/transform.scm: module/sxml/upstream/SXPath-old.scm: module/sxml/upstream/SSAX.scm:; module/language/tree-il/primitives.scm: module/language/ecmascript/base.scm: module/language/assembly/disassemble.scm: module/rnrs/bytevectors.scm: And also change the defmacro forms in, --------------------------------------------------------- module/system/base/lalr.scm: module/texinfo/reflection.scm: module/ice-9/and-let-star.scm: module/ice-9/calling.scm: module/ice-9/expect.scm: module/ice-9/optargs.scm: module/scripts/generate-autoload.scm: module/scripts/generate-autoload.scm: /Stefan On Mon, Sep 9, 2013 at 11:34 PM, Ian Price <ianprice90@googlemail.com>wrote: > Stefan Israelsson Tampe <stefan.itampe@gmail.com> writes: > > > (use-modules (srfi srfi-2)) > > (use-modules (srfi srfi-1)) > > > > (define-curried (string-matches pattern string) > > (and-let* ((match-struct (string-match pattern string)) > > (count (match:count match-struct))) > > (map (lambda(n)(match:substring match-struct n)) > > (iota (1- count) 1)))) > > > > scheme@(guile-user)> (string-matches "([a-z])" "a") > > $4 = ("a") > > > > > > ,exp ((string-matches "([a-z])") "a") > > $5 = ((lambda (string-871) > > (let ((match-struct-876 > > (string-match "([a-z])" string))) > > (if match-struct-876 > > (let ((count-880 (match:count match-struct-876))) > > (if count-880 > > (map (lambda (n-883) > > (match:substring match-struct-876 n-883)) > > (iota (#{1-}# count-880) 1)) > > #f)) > > #f))) > > "a") > > > > And we see that string is not managed correctly. Is this a bug? I > > can't understand why this is not treated as intended! > > The problem is one that occurs when hygienic and non-hygienic macros are > mixed. Here, and-let* is the non-hygienic one. Basically, in a hygienic > macro, you carry around a bunch of context to allow you to refer to the > right variable names. Then defmacro comes along, strips all that away, > and uses the raw symbol names. > > We can fix and-let* to be hygienic, that's pretty easy, but I'm not sure > what you can do about this in general. It's not like you can pass in the > gensymed name, because that will break the way people who for some > reason still right defmacros expect them to work. > > Dunno, maybe I'm just missing some insight here. > > -- > Ian Price -- shift-reset.com > > "Programming is like pinball. The reward for doing it well is > the opportunity to do it again" - from "The Wizardy Compiled" > [-- Attachment #2: Type: text/html, Size: 4799 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: and-let* is not composable? 2013-09-10 13:42 ` Stefan Israelsson Tampe @ 2013-09-10 13:51 ` Ian Price 0 siblings, 0 replies; 14+ messages in thread From: Ian Price @ 2013-09-10 13:51 UTC (permalink / raw) To: Stefan Israelsson Tampe; +Cc: guile-user, guile-devel Stefan Israelsson Tampe <stefan.itampe@gmail.com> writes: > Ahh, I thought we nuked the unhygienic macros in our tool-chain, That > is a BUG! It is, but the issue is going to be around for as long as Guile users themselves write unhygienic macros. And it is important to note that just because you are using syntax-case[0], doesn't mean you are writing hygienic macros. One of my first patches to Guile was hygiene fixes in the define-record-type form in (rnrs records syntactic). Fixing it in our own backyard is a start, but there is no need to do it all at once, and it could be some nice low hanging fruit for new contributors. > Anyway can I suggest a Bug report where we Then add it to the tracker :) 0. syntax-rules is fine, modulo al petrofsky/oleg style craziness -- Ian Price -- shift-reset.com "Programming is like pinball. The reward for doing it well is the opportunity to do it again" - from "The Wizardy Compiled" ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: and-let* is not composable? 2013-09-09 21:34 ` Ian Price 2013-09-10 13:42 ` Stefan Israelsson Tampe @ 2013-11-02 2:39 ` Ian Price 2013-11-02 19:01 ` Ian Price 1 sibling, 1 reply; 14+ messages in thread From: Ian Price @ 2013-11-02 2:39 UTC (permalink / raw) To: guile-user Ian Price <ianprice90@googlemail.com> writes: > The problem is one that occurs when hygienic and non-hygienic macros are > mixed. Here, and-let* is the non-hygienic one. Basically, in a hygienic > macro, you carry around a bunch of context to allow you to refer to the > right variable names. Then defmacro comes along, strips all that away, > and uses the raw symbol names. > > We can fix and-let* to be hygienic, that's pretty easy, but I'm not sure > what you can do about this in general. It's not like you can pass in the > gensymed name, because that will break the way people who for some > reason still right defmacros expect them to work. > > Dunno, maybe I'm just missing some insight here. This discussion came up on #scheme yesterday, and Eli Barzilay mentioned that Racket tries to be more clever with its legacy defmacro by keeping a hash associating input sexps with their syntax objects. This is in no way a silver bullet, but for many macros, this is liable to work out really nicely. An example implementation is below, though I have a few notes indicating things needing fixed. (define-syntax define-macro (lambda (x) "Define a defmacro." (syntax-case x () ((_ (macro . args) doc body1 body ...) (string? (syntax->datum #'doc)) #'(define-macro macro doc (lambda args body1 body ...))) ((_ (macro . args) body ...) #'(define-macro macro #f (lambda args body ...))) ((_ macro transformer) #'(define-macro macro #f transformer)) ((_ macro doc transformer) (or (string? (syntax->datum #'doc)) (not (syntax->datum #'doc))) #'(define-syntax macro (lambda (y) (define (recontextualize form context default) (define (walk x) ;; is there any possibility of a circular syntax object? (cond ((hashv-ref context x) => (lambda (x) x)) ((pair? x) (cons (walk (car x)) (walk (cdr x)))) ((vector? x) (vector-map walk x)) ((symbol? x) (datum->syntax default x)) (else x))) (walk form)) (define (build-context form stx-form) (define ctx (make-hash-table)) (define (walk x y) (hashv-set! ctx x y) ;; is there any possibility of a circular syntax object? (cond ((pair? x) (walk (car x) (car (syntax-e y))) (walk (cdr x) (cdr (syntax-e y)))) ((vector? x) (vector-for-each2 walk x (syntax-e y))) ;; Any other types needing handled? )) (walk form stx-form) ctx) (define (vector-for-each2 f v1 v2) (define len (vector-length v1)) (define v* (make-vector len)) (let loop ((i 0)) (unless (= i len) (vector-set! v* i (f (vector-ref v1 i) (vector-ref v2 i))) (loop (+ i 1)))) v*) (define (vector-map f v) (define len (vector-length v)) (define v* (make-vector len)) (let loop ((i 0)) (unless (= i len) (vector-set! v* i (f (vector-ref v i))) (loop (+ i 1)))) v*) (define (syntax-e obj) (syntax-case obj () [(first . rest) (cons #'first #'rest)] [#(value (... ...)) (apply vector #'(value (... ...)))] [a (syntax->datum #'a)])) doc ;; FIXME: may not be a docstring, and so would fail above #((macro-type . defmacro) (defmacro-args args)) (syntax-case y () ((_ . args) (let* ((v (syntax->datum #'args)) (ctx (build-context v #'args))) (recontextualize (apply transformer v) ctx y)))))))))) This version of define-macro still fails on the original macros as posted by Panicz Maciej Godek, but gives the "right" result using stis's ck macro version. At 2:30am, I'm not liable to get to the bottom of why till tomorrow, but I think doing something like this is a positive step. -- Ian Price -- shift-reset.com "Programming is like pinball. The reward for doing it well is the opportunity to do it again" - from "The Wizardy Compiled" ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: and-let* is not composable? 2013-11-02 2:39 ` Ian Price @ 2013-11-02 19:01 ` Ian Price 0 siblings, 0 replies; 14+ messages in thread From: Ian Price @ 2013-11-02 19:01 UTC (permalink / raw) To: guile-user [-- Attachment #1: Type: text/plain, Size: 1248 bytes --] Ian Price <ianprice90@googlemail.com> writes: > This version of define-macro still fails on the original macros as > posted by Panicz Maciej Godek, but gives the "right" result using stis's > ck macro version. > > At 2:30am, I'm not liable to get to the bottom of why till tomorrow, but > I think doing something like this is a positive step. Turns out it was PEBKAC /tmp $ guile -q GNU Guile 2.0.9.95-c9e3-dirty Copyright (C) 1995-2013 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@(guile-user)> (include "/tmp/defmacrofix.scm") scheme@(guile-user)> ,expand ((string-matches "([a-z])") "a") $1 = (let* ((string "a") (match-struct (string-match "([a-z])" string))) (and match-struct (let ((count (match:count match-struct))) (and count (map (lambda (n) (match:substring match-struct n)) (iota (#{1-}# count) 1)))))) -- Ian Price -- shift-reset.com "Programming is like pinball. The reward for doing it well is the opportunity to do it again" - from "The Wizardy Compiled" [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: defmacro example --] [-- Type: text/x-scheme, Size: 4555 bytes --] (use-modules (ice-9 match) (srfi srfi-1)) (define-syntax define-macro (lambda (x) "Define a defmacro." (syntax-case x () ((_ (macro . args) doc body1 body ...) (string? (syntax->datum #'doc)) #'(define-macro macro doc (lambda args body1 body ...))) ((_ (macro . args) body ...) #'(define-macro macro #f (lambda args body ...))) ((_ macro transformer) #'(define-macro macro #f transformer)) ((_ macro doc transformer) (or (string? (syntax->datum #'doc)) (not (syntax->datum #'doc))) #`(define-syntax macro (lambda (y) #,@(if (string? (syntax->datum #'doc)) (list #'doc) '()) (define (recontextualize form context default) (define (walk x) ;; is there any possibility of a circular syntax object? (cond ((hashv-ref context x) => (lambda (x) x)) ((pair? x) (cons (walk (car x)) (walk (cdr x)))) ((vector? x) (vector-map walk x)) ((symbol? x) (datum->syntax default x)) (else x))) (walk form)) (define (build-context form stx-form) (define ctx (make-hash-table)) (define (walk x y) (hashv-set! ctx x y) ;; is there any possibility of a circular syntax object? (cond ((pair? x) (walk (car x) (car (syntax-e y))) (walk (cdr x) (cdr (syntax-e y)))) ((vector? x) (vector-for-each2 walk x (syntax-e y))))) (walk form stx-form) ctx) (define (vector-for-each2 f v1 v2) (define len (vector-length v1)) (define v* (make-vector len)) (let loop ((i 0)) (unless (= i len) (vector-set! v* i (f (vector-ref v1 i) (vector-ref v2 i))) (loop (+ i 1)))) v*) (define (vector-map f v) (define len (vector-length v)) (define v* (make-vector len)) (let loop ((i 0)) (unless (= i len) (vector-set! v* i (f (vector-ref v i))) (loop (+ i 1)))) v*) (define (syntax-e obj) (syntax-case obj () [(first . rest) (cons #'first #'rest)] [#(value (... ...)) (apply vector #'(value (... ...)))] [a (syntax->datum #'a)])) #((macro-type . defmacro) (defmacro-args args)) (syntax-case y () ((_ . args) (let* ((v (syntax->datum #'args)) (ctx (build-context v #'args))) (recontextualize (apply transformer v) ctx y)))))))))) (define-macro (and-let* vars . body) (define (expand vars body) (cond ((null? vars) (if (null? body) #t `(begin ,@body))) ((pair? vars) (let ((exp (car vars))) (cond ((pair? exp) (cond ((null? (cdr exp)) `(and ,(car exp) ,(expand (cdr vars) body))) (else (let ((var (car exp))) `(let (,exp) (and ,var ,(expand (cdr vars) body))))))) (else `(and ,exp ,(expand (cdr vars) body)))))) (else (error "not a proper list" vars)))) (expand vars body)) (define-macro (define-curried signature . body) (match signature ((name args ...) `(define-syntax ,name (syntax-rules () ((_ ,@args) (begin ,@body)) ,@(let loop ((args* args)) (match args* (() '()) ((first ... last) (cons `((_ ,@first #;...) (lambda(,last)(,name ,@args*))) (loop first #;...)))))))))) (define-curried (matches? pattern x) (match x (pattern #t) (else #f))) (define-curried (string-matches pattern string) ;;CAUTION: buggy version (and-let* ((match-struct (string-match pattern string)) (count (match:count match-struct))) (map (lambda(n)(match:substring match-struct n)) (iota (1- count) 1)))) ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: and-let* is not composable? 2013-09-09 17:35 and-let* is not composable? Panicz Maciej Godek 2013-09-09 20:26 ` Stefan Israelsson Tampe @ 2013-09-10 17:57 ` Ian Price 2013-09-11 12:25 ` Panicz Maciej Godek 1 sibling, 1 reply; 14+ messages in thread From: Ian Price @ 2013-09-10 17:57 UTC (permalink / raw) To: Panicz Maciej Godek; +Cc: guile-user@gnu.org I have a few notes unrelated to those I've already mentioned in the thread. 1. Guile already has curried definitions in (ice-9 curried-definitions) 2. By turning your functions definitions into a macro, you've lost the ability to use them in a first class manner. 3. I would strongly recommend _not_ using define-macro, and even more so, not mixing define-macro and syntax-rules in a macro. Syntax-case is as powerful as defmacro, and in most practical cases, about as much writing. 4. What your macro does is not currying, it is partial application. Those are different things. Currying refers to the process of turning a function of n arguments into an n-nested set of 1 argument functions. Partial application is, well, not supplying all the arguments to a function. You can have partial application without currying, as your macro shows. -- Ian Price -- shift-reset.com "Programming is like pinball. The reward for doing it well is the opportunity to do it again" - from "The Wizardy Compiled" ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: and-let* is not composable? 2013-09-10 17:57 ` Ian Price @ 2013-09-11 12:25 ` Panicz Maciej Godek 2013-09-11 14:05 ` Ian Price 0 siblings, 1 reply; 14+ messages in thread From: Panicz Maciej Godek @ 2013-09-11 12:25 UTC (permalink / raw) To: Panicz Maciej Godek, guile-user@gnu.org, Ian Price [-- Attachment #1: Type: text/plain, Size: 2113 bytes --] 2013/9/10 Ian Price <ianprice90@googlemail.com> > > I have a few notes unrelated to those I've already mentioned in the > thread. > > 1. Guile already has curried definitions in (ice-9 curried-definitions) > > Yes, but those are completely different. I wouldn't call "define-curried" a curried definition, but a definition of a curried macro for generating procedures. > 2. By turning your functions definitions into a macro, you've lost the > ability to use them in a first class manner. > > That's not entirely true. I just need an extra pair of parentheses to do so. The semantics is certainly different than the one of Guile's curried definitions, but that's fine. > 3. I would strongly recommend _not_ using define-macro, and even more > so, not mixing define-macro and syntax-rules in a macro. Syntax-case is > as powerful as defmacro, and in most practical cases, about as much > writing. > Well, while syntax-rules macros are quite easy to understand (at least from the user's point of view), although sometimes a little tricky, the syntax-case system I find still too difficult to use. define-macro, on the other hand, is very easy to explain even to beginner programmers, although the resulting macros are much more difficult to analyse. The main problem with syntax-rules/syntax-case macros is the treatment of ellipses, which makes it difficult to create macros that create macros. Still, I didn't post to comp.lang.scheme for no reason :) 4. What your macro does is not currying, it is partial > application. Those are different things. Currying refers to the process > of turning a function of n arguments into an n-nested set of 1 argument > functions. Partial application is, well, not supplying all the arguments > to a function. You can have partial application without currying, as > your macro shows. > OK, you got me here. But it's still difficult for me to come up with a good name for that macro (and if so, the Guile's curried definitions are not curried either, at least in general: because you can (define ((f a b) c d) ...), getting a chain of 2-argument functions) Thanks, M. [-- Attachment #2: Type: text/html, Size: 3415 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: and-let* is not composable? 2013-09-11 12:25 ` Panicz Maciej Godek @ 2013-09-11 14:05 ` Ian Price 2013-09-13 18:40 ` Panicz Maciej Godek 2013-10-04 22:27 ` Panicz Maciej Godek 0 siblings, 2 replies; 14+ messages in thread From: Ian Price @ 2013-09-11 14:05 UTC (permalink / raw) To: Panicz Maciej Godek; +Cc: guile-user@gnu.org Panicz Maciej Godek <godek.maciek@gmail.com> writes: > That's not entirely true. I just need an extra pair of parentheses > to do so. The semantics is certainly different than the one of > Guile's curried definitions, but that's fine. An identifier macro would be even better, but it's still not first class. > Well, while syntax-rules macros are quite easy to understand > (at least from the user's point of view), although sometimes a little > tricky, the syntax-case system I find still too difficult to use. > define-macro, on the other hand, is very easy to explain > even to beginner programmers, although the resulting macros > are much more difficult to analyse. If you, or the other people who are confused by syntax-case, can point to the parts of the manual that confuse you, so we can clear them up, I think we'd all appreciate it. Fundamentally, syntax-case shouldn't be harder to use than define-macro 99% of the time, if you remember - macros are functions from "syntax-objects" to syntax-objects - syntax-objects are smart symbols - syntax->datum to remove the smartness - datum->syntax is for when you want to break hygiene (but syntax parameters are better where applicable) - use quasisyntax to construct lists of syntax-objects instead of quasiquote to construct lists of symbols. > The main problem with syntax-rules/syntax-case macros is > the treatment of ellipses, which makes it difficult to create > macros that create macros. You can expand into ellipses with (... ...), it's ugly, but it's there. > with a good name for that macro (and if so, the Guile's curried > definitions are not curried either, at least in general: because > you can (define ((f a b) c d) ...), getting a chain of 2-argument > functions) Indeed, I wasn't sure whether or not I should have mentioned it, but I was told this is the "correct" name for that feature on comp.lang.scheme a while back. I think it might have been Will Clinger, but I'd need to double check. -- Ian Price -- shift-reset.com "Programming is like pinball. The reward for doing it well is the opportunity to do it again" - from "The Wizardy Compiled" ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: and-let* is not composable? 2013-09-11 14:05 ` Ian Price @ 2013-09-13 18:40 ` Panicz Maciej Godek 2013-09-14 8:19 ` Stefan Israelsson Tampe 2013-10-04 22:27 ` Panicz Maciej Godek 1 sibling, 1 reply; 14+ messages in thread From: Panicz Maciej Godek @ 2013-09-13 18:40 UTC (permalink / raw) To: guile-user@gnu.org, Ian Price [-- Attachment #1: Type: text/plain, Size: 5345 bytes --] > Well, while syntax-rules macros are quite easy to understand > > (at least from the user's point of view), although sometimes a little > > tricky, the syntax-case system I find still too difficult to use. > > define-macro, on the other hand, is very easy to explain > > even to beginner programmers, although the resulting macros > > are much more difficult to analyse. > If you, or the other people who are confused by syntax-case, can point > to the parts of the manual that confuse you, so we can clear them up, I > think we'd all appreciate it. > > The order of presentation in the manual is that syntax-rules macros are explained first, because they are more limited, and there's no need to get into details regarding syntax objects. I used to explain the "syntax-rules" macros to my friends by first showing "what we want to transform into what", and then packing it up in some magic to work (because basically that's how I understand them). The advantage was that the meaning of the macro is readily visible and there's no need to analyse the code. The manual however makes very little reference to define-macro system to explain how syntax-case works. It is easy to see that lisp programs are lists of symbols, and so they can be processed like any other lists of symbols before they are evaluated -- that's the essentials of lisp macros. In case of "syntax-case" some new mysterious notions appear: namely, the unfamous syntax objects. And while it is easy to imagine how the list of symbols look like, all we read about in the manual is that "the syntax expander represents identifiers as annotated syntax objects, attaching such information to those syntax objects as is needed to maintain referential transparency". We don't know what sort of information is that, and why is it better to use syntax-case over define-macro. Especially if we compare the "anamorphic if" definition from the manual, (define-syntax aif (lambda (x) (syntax-case x () ((_ test then else) (syntax-case (datum->syntax x 'it) () (it #'(let ((it test)) (if it then else)))))))) with the most straightforward "anamorphic if" with define-macro: (define-macro (aif test then else) `(let ((it ,test)) (if it ,then ,else))) (Even if the latter is not exactly right, as is argued in section 6.10.5, it generally does its job) Also, I have to admit that I still don't understand what the syntax-case macro above is doing. Furthermore, it isn't clear why syntax->datum takes only one argument, and datum->syntax takes two (a syntax object and the "datum" itself) > Fundamentally, syntax-case shouldn't be harder to use than define-macro > 99% of the time, if you remember > > - macros are functions from "syntax-objects" to syntax-objects > - syntax-objects are smart symbols > - syntax->datum to remove the smartness > - datum->syntax is for when you want to break hygiene (but syntax > parameters are better where applicable) > - use quasisyntax to construct lists of syntax-objects instead of > quasiquote to construct lists of symbols. > Maybe showing a sufficient number of examples would be more helpful, because I haven't got a clue "where syntax parameters are applicable" or how I could use quasisyntax and unsyntax. By the way, I've had another problem when defining a macro. I've been trying to implement something like dynamical scoping, but such that it wouldn't require introducing global variables The idea is that one can write (with-default ((a 5)) (define (f x) (+ x (specific a)))) and that later the procedure could be used like that: (f 10) ===> 15 (specify ((a 20)) (f 10)) ===> 30 The first attempt was that there could be a global hash table *specifics* that would store the lists of values, and that "specific" could be a local macro that would expand to a hash reference. So I tried to implement that using syntax-rules in the following way: (define-syntax with-default (syntax-rules () ((_ ((name value) ...) actions ...) (let-syntax ((specific (syntax-rules (name ...) ((_ name) (hash-ref *specifics* 'name value)) ;; don't mind shadowing for now ...))) actions ...)))) However, when I tried to use it, it seemed that the let-syntax behaves as if it wasn't there (so it didn't behave at all!): (with-default ((a 5)) (define (f x)(+ x (specific a)))) The situation was (again) resolved by creating a nasty combination of "define-macro" and "syntax-rules", namely: (define-macro (with-default bindings . actions) (match bindings (((names values) ...) `(let-syntax ((specific (syntax-rules ,names ,@(map (match-lambda ((name value) `((_ ,name) (hash-ref *defaults* ',name ,value)))) bindings)))) ,@actions)))) It's not that I like it. I really don't. But I don't know how how else this could be achieved, nor why the former solution so stubbornly resists to work. Thanks, M. [-- Attachment #2: Type: text/html, Size: 7787 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: and-let* is not composable? 2013-09-13 18:40 ` Panicz Maciej Godek @ 2013-09-14 8:19 ` Stefan Israelsson Tampe 0 siblings, 0 replies; 14+ messages in thread From: Stefan Israelsson Tampe @ 2013-09-14 8:19 UTC (permalink / raw) To: Panicz Maciej Godek; +Cc: guile-user@gnu.org, Ian Price [-- Attachment #1: Type: text/plain, Size: 6718 bytes --] Hi, Yes I agree that we should have good documentation to show how to implement more advanced macros. The main reason (I beleve) for the "experts" to spend time using syntax-case let-syntax syntax-parameters etc is for yielding a user friendly framework to the user who will typically use syntax-rules. If we would stick with defmacros or define-macro we would push the burden to the novice end user and not to the expert. Now this is what hygiene buy's you, and what it cost. Sticking to define-macros would mean that we will get plenty of bug's caused by leaking macros and we would spend a considerable time on guile-user and irc to hunt down these bug's. Also In guile and most schemes we have standardized on this setup and not complying to the standard essentially means that you are out of thee cooperation. But I think that your good point is that we should create/link to good documentation for the syntax-case system and essentially explain the model one needs to have in the head in order to use it effectively. Regards Stefan On Fri, Sep 13, 2013 at 8:40 PM, Panicz Maciej Godek <godek.maciek@gmail.com > wrote: > > Well, while syntax-rules macros are quite easy to understand > >> > (at least from the user's point of view), although sometimes a little >> > tricky, the syntax-case system I find still too difficult to use. >> > define-macro, on the other hand, is very easy to explain >> > even to beginner programmers, although the resulting macros >> > are much more difficult to analyse. >> If you, or the other people who are confused by syntax-case, can point >> to the parts of the manual that confuse you, so we can clear them up, I >> think we'd all appreciate it. >> >> > The order of presentation in the manual is that syntax-rules macros > are explained first, because they are more limited, and there's no > need to get into details regarding syntax objects. > > I used to explain the "syntax-rules" macros to my friends by > first showing "what we want to transform into what", and then packing > it up in some magic to work (because basically that's how I > understand them). The advantage was that the meaning of the macro > is readily visible and there's no need to analyse the code. > > The manual however makes very little reference to define-macro system > to explain how syntax-case works. It is easy to see that lisp programs > are lists of symbols, and so they can be processed like any other > lists of symbols before they are evaluated -- that's the essentials > of lisp macros. In case of "syntax-case" some new mysterious > notions appear: namely, the unfamous syntax objects. And while it > is easy to imagine how the list of symbols look like, all we read > about in the manual is that "the syntax expander represents > identifiers as annotated syntax objects, attaching such information > to those syntax objects as is needed to maintain referential > transparency". We don't know what sort of information is that, and > why is it better to use syntax-case over define-macro. > > Especially if we compare the "anamorphic if" definition from the > manual, > (define-syntax aif > (lambda (x) > (syntax-case x () > ((_ test then else) > (syntax-case (datum->syntax x 'it) () > (it > #'(let ((it test)) > (if it then else)))))))) > > with the most straightforward "anamorphic if" with define-macro: > > (define-macro (aif test then else) > `(let ((it ,test)) > (if it > ,then > ,else))) > > (Even if the latter is not exactly right, as is argued in section > 6.10.5, it generally does its job) > > Also, I have to admit that I still don't understand what the > syntax-case macro above is doing. Furthermore, it isn't clear why > syntax->datum takes only one argument, and datum->syntax takes > two (a syntax object and the "datum" itself) > > > >> Fundamentally, syntax-case shouldn't be harder to use than define-macro >> 99% of the time, if you remember >> >> - macros are functions from "syntax-objects" to syntax-objects >> - syntax-objects are smart symbols >> - syntax->datum to remove the smartness >> - datum->syntax is for when you want to break hygiene (but syntax >> parameters are better where applicable) >> - use quasisyntax to construct lists of syntax-objects instead of >> quasiquote to construct lists of symbols. >> > > > Maybe showing a sufficient number of examples would be > more helpful, because I haven't got a clue "where syntax > parameters are applicable" or how I could use quasisyntax > and unsyntax. > > By the way, I've had another problem when defining a macro. > I've been trying to implement something like dynamical scoping, > but such that it wouldn't require introducing global variables > > The idea is that one can write > > (with-default ((a 5)) > (define (f x) (+ x (specific a)))) > > and that later the procedure could be used like that: > (f 10) > ===> 15 > (specify ((a 20)) > (f 10)) > ===> 30 > > The first attempt was that there could be a global hash table > *specifics* that would store the lists of values, and that > "specific" could be a local macro that would expand to a hash > reference. So I tried to implement that using syntax-rules > in the following way: > > (define-syntax with-default > (syntax-rules () > ((_ ((name value) ...) > actions ...) > (let-syntax ((specific (syntax-rules (name ...) > ((_ name) > (hash-ref *specifics* 'name value)) > ;; don't mind shadowing for now > ...))) > actions ...)))) > > However, when I tried to use it, it seemed that the let-syntax > behaves as if it wasn't there (so it didn't behave at all!): > > (with-default ((a 5)) > (define (f x)(+ x (specific a)))) > > The situation was (again) resolved by creating a nasty combination > of "define-macro" and "syntax-rules", namely: > > (define-macro (with-default bindings . actions) > (match bindings > (((names values) ...) > `(let-syntax ((specific (syntax-rules ,names > ,@(map (match-lambda > ((name value) > `((_ ,name) > (hash-ref *defaults* > ',name ,value)))) > bindings)))) > ,@actions)))) > > It's not that I like it. I really don't. But I don't know how how > else this could be achieved, nor why the former solution > so stubbornly resists to work. > > Thanks, > M. > > [-- Attachment #2: Type: text/html, Size: 9658 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: and-let* is not composable? 2013-09-11 14:05 ` Ian Price 2013-09-13 18:40 ` Panicz Maciej Godek @ 2013-10-04 22:27 ` Panicz Maciej Godek 2013-10-05 8:00 ` Ian Price 1 sibling, 1 reply; 14+ messages in thread From: Panicz Maciej Godek @ 2013-10-04 22:27 UTC (permalink / raw) To: Panicz Maciej Godek, guile-user@gnu.org [-- Attachment #1: Type: text/plain, Size: 1498 bytes --] > > If you, or the other people who are confused by syntax-case, can point > to the parts of the manual that confuse you, so we can clear them up, I > think we'd all appreciate it. > > Fundamentally, syntax-case shouldn't be harder to use than define-macro > 99% of the time, if you remember > As a little addendum to that topic, I just ran across Matthew Flatt's presentation of Racket macro system, http://www.infoq.com/presentations/racket, where he points to a quite good introduction to "define-syntax" macros by Greg Hendershott: http://www.greghendershott.com/fear-of-macros/ And now that my head is clearer on this regard, I think it is confusing to call them "syntax-case" macros, because "syntax-case" is rather auxiliary than essential for defining that sort of macro. The order of presentation in the manual resembles the one that Hendershott criticises, namely: first, the syntax-rules macros are presented, which are an epiphany once you comprehend them, and then syntax-case is presented as a different type of macro. However, syntax-case is only a pleasant way to destructure syntactic information passed to a transformer, and the same effect could be achieved without using it, by using some more explicit procedures like syntax->datum. (Furthermore, Hendershott explains more specifically how do the syntax objects differ from raw scheme forms). So I believe that at least placing a link to than explanation in the manual could be a great help for beginners. Best regards, M. [-- Attachment #2: Type: text/html, Size: 2210 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: and-let* is not composable? 2013-10-04 22:27 ` Panicz Maciej Godek @ 2013-10-05 8:00 ` Ian Price 0 siblings, 0 replies; 14+ messages in thread From: Ian Price @ 2013-10-05 8:00 UTC (permalink / raw) To: Panicz Maciej Godek; +Cc: guile-user@gnu.org Panicz Maciej Godek <godek.maciek@gmail.com> writes: > presentation of Racket macro > system, http://www.infoq.com/presentations/racket, > where he points to a quite good introduction to "define-syntax" macros > by Greg Hendershott: http://www.greghendershott.com/fear-of-macros/ I was also made aware of it recently, it seems like a fine introduction, and I had even considered mentioning it to you in this thread :) but... > And now that my head is clearer on this regard, I think it is > confusing > to call them "syntax-case" macros, because "syntax-case" is rather > auxiliary than essential for defining that sort of macro. (This is mostly true, but it's been a conventional name for some 20 years :/) > So I believe that at least placing a link to than explanation in the > manual > could be a great help for beginners. ... the problem is it makes a lot of references to Racket, and Racket specific ways of doing things. Much of it can be fixed for Guile and other Schemes, but you'd still need to gut around half of it. There haven't been any patches yet, but that doesn't mean I haven't been spending time thinking about all this :) -- Ian Price -- shift-reset.com "Programming is like pinball. The reward for doing it well is the opportunity to do it again" - from "The Wizardy Compiled" ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2013-11-02 19:01 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2013-09-09 17:35 and-let* is not composable? Panicz Maciej Godek 2013-09-09 20:26 ` Stefan Israelsson Tampe 2013-09-09 21:34 ` Ian Price 2013-09-10 13:42 ` Stefan Israelsson Tampe 2013-09-10 13:51 ` Ian Price 2013-11-02 2:39 ` Ian Price 2013-11-02 19:01 ` Ian Price 2013-09-10 17:57 ` Ian Price 2013-09-11 12:25 ` Panicz Maciej Godek 2013-09-11 14:05 ` Ian Price 2013-09-13 18:40 ` Panicz Maciej Godek 2013-09-14 8:19 ` Stefan Israelsson Tampe 2013-10-04 22:27 ` Panicz Maciej Godek 2013-10-05 8:00 ` Ian Price
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).