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