public inbox for kawa@sourceware.org
 help / color / mirror / Atom feed
* conditional bindings
@ 2014-12-09  5:48 Per Bothner
  2014-12-09 21:16 ` Jamison Hope
  0 siblings, 1 reply; 10+ messages in thread
From: Per Bothner @ 2014-12-09  5:48 UTC (permalink / raw)
  To: kawa mailing list

I checked in a little feature, which you can think of as
a precursor to pattern matching.

The syntax (? NAME::TYPE VALUE) can be used in conditional forms:
if, and, cond, etc.  First VALUE is evaluated.  Then if the result
is an compatible with TYPE, then NAME is bound to the result
(possibly after conversion).  The result is #t, but with the "side effect"
that NAME is lexically bound in "following forms".  If VALUE doesn't
match TYPE then the ?-form is false.

You can you a ? form in an (and e1 e2 ...). Then a binding in e1
is visible e2 and so on.  Similar in (if e1 e2 e3) any binding in e1
is visible in e2.  For example:

(if (and (? x::integer e1) (? y::integer e2) (>= x 0) (>= y 0))
   (+ x y)
  (error))

In the future I intend to allow more general patterns: (? PATTERN VALUE)

If the VALUE is lazy, it is automatically forced (unless TYPE
is Object or implements gnu.mapping.Lazy).
-- 
	--Per Bothner
per@bothner.com   http://per.bothner.com/

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

* Re: conditional bindings
  2014-12-09  5:48 conditional bindings Per Bothner
@ 2014-12-09 21:16 ` Jamison Hope
  2014-12-10  3:25   ` Per Bothner
  0 siblings, 1 reply; 10+ messages in thread
From: Jamison Hope @ 2014-12-09 21:16 UTC (permalink / raw)
  To: kawa mailing list

On Dec 9, 2014, at 12:48 AM, Per Bothner <per@bothner.com> wrote:

> The syntax (? NAME::TYPE VALUE) can be used in conditional forms:
> if, and, cond, etc.  First VALUE is evaluated.  Then if the result
> is an compatible with TYPE, then NAME is bound to the result
> (possibly after conversion).  The result is #t, but with the "side effect"
> that NAME is lexically bound in "following forms".  If VALUE doesn't
> match TYPE then the ?-form is false.

Is the result actually #t, or some unspecified true value?  Is something
like this supposed to work?

#|kawa:1|# (define (f x) (cond ((? r ::real x) => (lambda (y) y)) (else #f)))
/dev/stdin:1:22: '?' is only allowed in a conditional e.g. 'if' or 'and'


Types backed by Java primitives seem to be troublesome.  I get VerifyErrors
("Bad type on operand stack") with the following:

(define (f x) (if (? b ::boolean x) b #f))

(define (f x) (if (? c ::character x) c #\?))


What types of implicit conversions are to be expected?  I find the
difference between these to be slightly unintuitive.

#|kawa:5|# (map (lambda (x) (if (? i ::int x) i -1)) '(3 "hello" 3.5 #\?))
(3 -1 3 -1)
#|kawa:6|# (map (lambda (x) (if (? i ::integer x) i -1)) '(3 "hello" 3.5 #\?))
(3 -1 -1 -1)

Note that 3.5 becomes int 3, but not integer 3.


#|kawa:7|# (map (lambda (x) (if (? s ::string x) s "N/A")) '(3 "hello" 3.5 #\?))
(N/A hello N/A N/A)

but

#|kawa:8|# (map (lambda (x) (if (? s ::String x) s "N/A")) '(3 "hello" 3.5 #\?))
Exception in thread "main" java.lang.NoClassDefFoundError: String



--
Jamison Hope
The PTR Group
www.theptrgroup.com



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

* Re: conditional bindings
  2014-12-09 21:16 ` Jamison Hope
@ 2014-12-10  3:25   ` Per Bothner
  2014-12-11  4:05     ` Jamison Hope
  0 siblings, 1 reply; 10+ messages in thread
From: Per Bothner @ 2014-12-10  3:25 UTC (permalink / raw)
  To: kawa



On 12/09/2014 01:16 PM, Jamison Hope wrote:
> On Dec 9, 2014, at 12:48 AM, Per Bothner <per@bothner.com> wrote:
>
>> The syntax (? NAME::TYPE VALUE) can be used in conditional forms:
>> if, and, cond, etc.  First VALUE is evaluated.  Then if the result
>> is an compatible with TYPE, then NAME is bound to the result
>> (possibly after conversion).  The result is #t, but with the "side effect"
>> that NAME is lexically bound in "following forms".  If VALUE doesn't
>> match TYPE then the ?-form is false.
>
> Is the result actually #t, or some unspecified true value?

Well, (? NAME::TYPE VALUE) isn't actually an expression - it's more a
pseudo-expression or a "sub-clause" of and/if/etc.

> Is something like this supposed to work?
>
> #|kawa:1|# (define (f x) (cond ((? r ::real x) => (lambda (y) y)) (else #f)))
> /dev/stdin:1:22: '?' is only allowed in a conditional e.g. 'if' or 'and'

No, I don't think that makes sense.  However, the following does:

(define (f x) (cond ((? r ::real x) r) (else #f)))

> Types backed by Java primitives seem to be troublesome.  I get VerifyErrors
> ("Bad type on operand stack") with the following:
>
> (define (f x) (if (? b ::boolean x) b #f))
>
> (define (f x) (if (? c ::character x) c #\?))

Ouch.  I fixed those. I also fixed char and character-or-eof.
(The char type doesn't match for characters above #\xFFFF, but character does.)

> What types of implicit conversions are to be expected?  I find the
> difference between these to be slightly unintuitive.
>
> #|kawa:5|# (map (lambda (x) (if (? i ::int x) i -1)) '(3 "hello" 3.5 #\?))
> (3 -1 3 -1)
> #|kawa:6|# (map (lambda (x) (if (? i ::integer x) i -1)) '(3 "hello" 3.5 #\?))
> (3 -1 -1 -1)
>
> Note that 3.5 becomes int 3, but not integer 3.

I think both of those should be false.

Likewise for 'short' should *not* match if the values is not in the 'short' range.

> #|kawa:7|# (map (lambda (x) (if (? s ::string x) s "N/A")) '(3 "hello" 3.5 #\?))
> (N/A hello N/A N/A)
>
> but
>
> #|kawa:8|# (map (lambda (x) (if (? s ::String x) s "N/A")) '(3 "hello" 3.5 #\?))
> Exception in thread "main" java.lang.NoClassDefFoundError: String

"String" is a special kind of type - which always matches.  I checked in a fix for this.

Btw I checked in some tests loosely based on your tests.
-- 
	--Per Bothner
per@bothner.com   http://per.bothner.com/

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

* Re: conditional bindings
  2014-12-10  3:25   ` Per Bothner
@ 2014-12-11  4:05     ` Jamison Hope
  2014-12-11  4:38       ` Per Bothner
  0 siblings, 1 reply; 10+ messages in thread
From: Jamison Hope @ 2014-12-11  4:05 UTC (permalink / raw)
  To: kawa@sourceware.org list

On Dec 9, 2014, at 10:24 PM, Per Bothner <per@bothner.com> wrote:

> On 12/09/2014 01:16 PM, Jamison Hope wrote:
>> What types of implicit conversions are to be expected?  I find the

>> difference between these to be slightly unintuitive.
>> 
>> #|kawa:5|# (map (lambda (x) (if (? i ::int x) i -1)) '(3 "hello" 3.5 #\?))
>> (3 -1 3 -1)
>> #|kawa:6|# (map (lambda (x) (if (? i ::integer x) i -1)) '(3 "hello" 3.5 #\?))
>> (3 -1 -1 -1)
>> 
>> Note that 3.5 becomes int 3, but not integer 3.
> 
> I think both of those should be false.
> 
> Likewise for 'short' should *not* match if the values is not in the 'short' range.

Agreed.  It looks like the compiler is just checking to see if x is a
Number, not an Integer (or IntNum), so all Numbers succeed in being
coerced to int.  Same with the rest of the primitive numeric types.

> 
>> #|kawa:7|# (map (lambda (x) (if (? s ::string x) s "N/A")) '(3 "hello" 3.5 #\?))
>> (N/A hello N/A N/A)
>> 
>> but
>> 
>> #|kawa:8|# (map (lambda (x) (if (? s ::String x) s "N/A")) '(3 "hello" 3.5 #\?))
>> Exception in thread "main" java.lang.NoClassDefFoundError: String
> 
> "String" is a special kind of type - which always matches.  I checked in a fix for this.

Interestingly, "void" also always matches.  And neither of them does any
coercion, although the String version does a Promise.force() on the
argument.

(define (f x) (if (? v ::void x) v (error))) =>

Attribute "Code", length:48, max_stack:1, max_locals:3, code_length:6
  0: aload_0
  1: astore_1
  2: aload_1
  3: astore_2
  4: aload_2
  5: areturn

(define (g x) (if (? s ::String x) s (error))) =>

Attribute "Code", length:55, max_stack:2, max_locals:4, code_length:13
  0: aload_0
  1: astore_1
  2: aload_1
  3: ldc <Class java.lang.String>
  5: invokestatic <Method gnu.mapping.Promise.force (java.lang.Object,java.lang.Class)java.lang.Object>
  8: astore_3
  9: aload_3
 10: astore_2
 11: aload_2
 12: areturn

Maybe a future optimization could remove some of the redundant
store/load pairs.  GSoC 2015?  :-)

> Btw I checked in some tests loosely based on your tests.

More interesting behavior, trying different variable names:

#|kawa:1|# (let ((x 0+0i)) (if (? r ::real x) r +nan.0)) ;; x, r works
0
#|kawa:2|# (let ((r 0+0i)) (if (? r ::real r) r +nan.0)) ;; r, r works
0
#|kawa:3|# (let ((x 0+0i)) (if (? ? ::real x) ? +nan.0)) ;; x, ? works
0
#|kawa:4|# (let ((? 0+0i)) (if (? r ::real ?) r +nan.0)) ;; ?, r fails
/dev/stdin:4:24: warning - no declaration seen for r
/dev/stdin:4:26: warning - no declaration seen for ::
/dev/stdin:4:17: warning - no declaration seen for r
/dev/stdin:4:24: unbound location: r
	at gnu.mapping.SharedLocation.get(SharedLocation.java:22)
	at gnu.mapping.DynamicLocation.get(DynamicLocation.java:28)
	at atInteractiveLevel$4.run(stdin:4)
	at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:317)
	at gnu.expr.ModuleExp.evalModule(ModuleExp.java:219)
	at kawa.Shell.run(Shell.java:291)
	at kawa.Shell.run(Shell.java:203)
	at kawa.Shell.run(Shell.java:184)
	at kawa.repl.main(repl.java:892)
#|kawa:5|# (let ((? 0+0i)) (if (? ? ::real ?) ? +nan.0)) ;; ?, ? fails differently
/dev/stdin:5:26: warning - no declaration seen for ::
/dev/stdin:5:26: unbound location: ::
	at gnu.mapping.SharedLocation.get(SharedLocation.java:22)
	at gnu.mapping.DynamicLocation.get(DynamicLocation.java:28)
	at atInteractiveLevel$5.run(stdin:5)
	at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:317)
	at gnu.expr.ModuleExp.evalModule(ModuleExp.java:219)
	at kawa.Shell.run(Shell.java:291)
	at kawa.Shell.run(Shell.java:203)
	at kawa.Shell.run(Shell.java:184)
	at kawa.repl.main(repl.java:892)

--
Jamison Hope
The PTR Group
www.theptrgroup.com



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

* Re: conditional bindings
  2014-12-11  4:05     ` Jamison Hope
@ 2014-12-11  4:38       ` Per Bothner
  2014-12-11 16:43         ` Jamison Hope
  0 siblings, 1 reply; 10+ messages in thread
From: Per Bothner @ 2014-12-11  4:38 UTC (permalink / raw)
  To: kawa



On 12/10/2014 08:04 PM, Jamison Hope wrote:

> More interesting behavior, trying different variable names:
>
> #|kawa:1|# (let ((x 0+0i)) (if (? r ::real x) r +nan.0)) ;; x, r works
> 0
> #|kawa:2|# (let ((r 0+0i)) (if (? r ::real r) r +nan.0)) ;; r, r works
> 0
> #|kawa:3|# (let ((x 0+0i)) (if (? ? ::real x) ? +nan.0)) ;; x, ? works
> 0
> #|kawa:4|# (let ((? 0+0i)) (if (? r ::real ?) r +nan.0)) ;; ?, r fails
> /dev/stdin:4:24: warning - no declaration seen for r
> /dev/stdin:4:26: warning - no declaration seen for ::
> /dev/stdin:4:17: warning - no declaration seen for r
> /dev/stdin:4:24: unbound location: r
> 	at gnu.mapping.SharedLocation.get(SharedLocation.java:22)
> 	at gnu.mapping.DynamicLocation.get(DynamicLocation.java:28)
> 	at atInteractiveLevel$4.run(stdin:4)
> 	at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:317)
> 	at gnu.expr.ModuleExp.evalModule(ModuleExp.java:219)
> 	at kawa.Shell.run(Shell.java:291)
> 	at kawa.Shell.run(Shell.java:203)
> 	at kawa.Shell.run(Shell.java:184)
> 	at kawa.repl.main(repl.java:892)
> #|kawa:5|# (let ((? 0+0i)) (if (? ? ::real ?) ? +nan.0)) ;; ?, ? fails differently
> /dev/stdin:5:26: warning - no declaration seen for ::
> /dev/stdin:5:26: unbound location: ::
> 	at gnu.mapping.SharedLocation.get(SharedLocation.java:22)
> 	at gnu.mapping.DynamicLocation.get(DynamicLocation.java:28)
> 	at atInteractiveLevel$5.run(stdin:5)
> 	at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:317)
> 	at gnu.expr.ModuleExp.evalModule(ModuleExp.java:219)
> 	at kawa.Shell.run(Shell.java:291)
> 	at kawa.Shell.run(Shell.java:203)
> 	at kawa.Shell.run(Shell.java:184)
> 	at kawa.repl.main(repl.java:892)

Looks correct to me ... If you redefine ? then it is just a regular identifier.
I.e.
   (let ((? 0+0i)) (if (? r ::real ?) r +nan.0))
is the same as:
   (let ((x 0+0i)) (if (x r ::real x) r +nan.0))

Remember - Scheme has no reserved identifiers.
-- 
	--Per Bothner
per@bothner.com   http://per.bothner.com/

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

* Re: conditional bindings
  2014-12-11  4:38       ` Per Bothner
@ 2014-12-11 16:43         ` Jamison Hope
  2014-12-11 18:37           ` Per Bothner
  0 siblings, 1 reply; 10+ messages in thread
From: Jamison Hope @ 2014-12-11 16:43 UTC (permalink / raw)
  To: kawa@sourceware.org list

On Dec 10, 2014, at 11:38 PM, Per Bothner <per@bothner.com> wrote:

> Looks correct to me ... If you redefine ? then it is just a regular identifier.
> I.e.
>  (let ((? 0+0i)) (if (? r ::real ?) r +nan.0))
> is the same as:
>  (let ((x 0+0i)) (if (x r ::real x) r +nan.0))
> 
> Remember - Scheme has no reserved identifiers.

Yeah I guess so, it just seems so.. unhygienic? dynamic?, for a macro to
work or not work depending upon the lexical scope of the call site.  It's
not like it was invoking the top-level-bound "?" anyway -- like you said,
(? NAME::TYPE VALUE) isn't an expression.

Plus, function keywords are immune to these shenanigans, so it seems like
macro literals should be, too.

(define (f #!key (x 0) (y 0)) (cons x y))
(let ((y: 7) (x: 1)) (f y: 2 x: 3))
=> (3 . 2)

But since that's not the case, I wonder if a macro literal renamer -- akin
to how (import (rename ...)) works for libraries -- could be useful.

It's already possible to wrap a macro to effectively remap its keywords:

(let-syntax ((if (lambda (x) (syntax-case x (q ::)
                               ((_ (q pattern :: type init) then else)
                                #'(if (? pattern :: type init) then else))))))
  (let ((? 3))
    (if (q r :: real ?) r 0)))

=> 3

but that's awfully verbose.

Maybe something like:

(syntax-rename macro (identifier1 identifier2) ...)

as in

(let-syntax ((if (syntax-rename if (? q))))
  (if (q r ::real ?) r +nan.0))

or

(define-syntax lisplike-cond (syntax-rename cond (else t)))

Do the original syntax-rules/syntax-case literal list and patterns
survive to runtime to facilitate the kind of introspection such a
thing would need?

--
Jamison Hope
The PTR Group
www.theptrgroup.com



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

* Re: conditional bindings
  2014-12-11 16:43         ` Jamison Hope
@ 2014-12-11 18:37           ` Per Bothner
  2014-12-11 22:43             ` Jamison Hope
  0 siblings, 1 reply; 10+ messages in thread
From: Per Bothner @ 2014-12-11 18:37 UTC (permalink / raw)
  To: kawa



On 12/11/2014 08:43 AM, Jamison Hope wrote:
> On Dec 10, 2014, at 11:38 PM, Per Bothner <per@bothner.com> wrote:
>
>> Looks correct to me ... If you redefine ? then it is just a regular identifier.
>> I.e.
>>   (let ((? 0+0i)) (if (? r ::real ?) r +nan.0))
>> is the same as:
>>   (let ((x 0+0i)) (if (x r ::real x) r +nan.0))
>>
>> Remember - Scheme has no reserved identifiers.
>
> Yeah I guess so, it just seems so.. unhygienic? dynamic?, for a macro to
> work or not work depending upon the lexical scope of the call site.

Quite the opposite.  A lexical binding overrides a non-lexical binding.
That is how Scheme lexical scoping and hygiene works.  Note it's the same for 'else':

(let ((else #f)) (cond (#f (display "a\n")) (else (display "b\n"))))

FWIW, I tested this on Guile as well as Kawa - neither prints anything.

> Plus, function keywords are immune to these shenanigans, so it seems like
> macro literals should be, too.
>
> (define (f #!key (x 0) (y 0)) (cons x y))
> (let ((y: 7) (x: 1)) (f y: 2 x: 3))
> => (3 . 2)

That's a bug, complicated by the attempt to provide compatibility with
Schemes not supporting keywords.

Kawa should either:
(a) complain about using a keywords as an identifier being bound in a let; or
(b) allow it, and then treat it as an identifier in the body.  I.e.
   (let ((y: 7) (x: 1)) (f y: 2 x: 3))
should either be illegal or the same as:
   (f 7 2 1 3)

(It should definitely do the latter if you specify --r7rs.)

> But since that's not the case, I wonder if a macro literal renamer -- akin
> to how (import (rename ...)) works for libraries -- could be useful.
>
> It's already possible to wrap a macro to effectively remap its keywords:
>
> (let-syntax ((if (lambda (x) (syntax-case x (q ::)
>                                 ((_ (q pattern :: type init) then else)
>                                  #'(if (? pattern :: type init) then else))))))
>    (let ((? 3))
>      (if (q r :: real ?) r 0)))
>
> => 3
>
> but that's awfully verbose.

Why would you want to do this?

> Do the original syntax-rules/syntax-case literal list and patterns
> survive to runtime to facilitate the kind of introspection such a
> thing would need?

Sort-of, mixed in with other literal values.
-- 
	--Per Bothner
per@bothner.com   http://per.bothner.com/

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

* Re: conditional bindings
  2014-12-11 18:37           ` Per Bothner
@ 2014-12-11 22:43             ` Jamison Hope
  2014-12-11 23:16               ` Per Bothner
  2014-12-12  5:07               ` Per Bothner
  0 siblings, 2 replies; 10+ messages in thread
From: Jamison Hope @ 2014-12-11 22:43 UTC (permalink / raw)
  To: kawa@sourceware.org list

On Dec 11, 2014, at 1:36 PM, Per Bothner <per@bothner.com> wrote:

> On 12/11/2014 08:43 AM, Jamison Hope wrote:
>> On Dec 10, 2014, at 11:38 PM, Per Bothner <per@bothner.com> wrote:
>> 
>>> Looks correct to me ... If you redefine ? then it is just a regular identifier.
>>> I.e.
>>>  (let ((? 0+0i)) (if (? r ::real ?) r +nan.0))
>>> is the same as:
>>>  (let ((x 0+0i)) (if (x r ::real x) r +nan.0))
>>> 
>>> Remember - Scheme has no reserved identifiers.
>> 
>> Yeah I guess so, it just seems so.. unhygienic? dynamic?, for a macro to
>> work or not work depending upon the lexical scope of the call site.
> 
> Quite the opposite.  A lexical binding overrides a non-lexical binding.
> That is how Scheme lexical scoping and hygiene works.  Note it's the same for 'else':
> 
> (let ((else #f)) (cond (#f (display "a\n")) (else (display "b\n"))))
> 
> FWIW, I tested this on Guile as well as Kawa - neither prints anything.

Yeah I know, rebinding 'else' like that is even an example in Dybvig's book.
I'm not questioning whether it behaves correctly w.r.t. the standard.

It just seems strange that the caller's environment affects whether the
invokation matches a pattern with a literal.  I have trouble seeing how

(let ((else #f)) (cond (#f (display "a\n")) (else (display "b\n"))))
=> #!void

is hygienic, but

(defmacro add (x y) `(+ ,x ,y))
(let ((+ -)) (add 1 2))
=> -1

is unhygienic.  In both cases, the behavior of the macro depends upon
the *caller* environment and not just the lexical environment of the
macro definition.


>> Plus, function keywords are immune to these shenanigans, so it seems like
>> macro literals should be, too.
>> 
>> (define (f #!key (x 0) (y 0)) (cons x y))
>> (let ((y: 7) (x: 1)) (f y: 2 x: 3))
>> => (3 . 2)
> 
> That's a bug, complicated by the attempt to provide compatibility with
> Schemes not supporting keywords.
> 
> Kawa should either:
> (a) complain about using a keywords as an identifier being bound in a let; or
> (b) allow it, and then treat it as an identifier in the body.  I.e.
>  (let ((y: 7) (x: 1)) (f y: 2 x: 3))
> should either be illegal or the same as:
>  (f 7 2 1 3)
> 
> (It should definitely do the latter if you specify --r7rs.)

With --r7rs it returns (0 . 0).  Actually, I can't figure out a way
to pass a keyword argument with --r7rs, although it didn't prevent
me from defining the function.

(f x: 3) => "unbound location: x:"

(let ((x: 3)) (f x: 3)) => (0 . 0)
(let ((x: 3)) (f 'x: 3)) => (0 . 0)

>> But since that's not the case, I wonder if a macro literal renamer -- akin
>> to how (import (rename ...)) works for libraries -- could be useful.
>> 
>> It's already possible to wrap a macro to effectively remap its keywords:
>> 
>> (let-syntax ((if (lambda (x) (syntax-case x (q ::)
>>                                ((_ (q pattern :: type init) then else)
>>                                 #'(if (? pattern :: type init) then else))))))
>>   (let ((? 3))
>>     (if (q r :: real ?) r 0)))
>> 
>> => 3
>> 
>> but that's awfully verbose.
> 
> Why would you want to do this?

For 'if' in particular, I probably never would.

But in principle, I can imagine accumulating various libraries with
macros, each of which make use of their own literal lists, and wanting
to have a way to reuse the logic of those macros without needing to be
careful to avoid accidentally using any of their literals as variable
names.

>> Do the original syntax-rules/syntax-case literal list and patterns
>> survive to runtime to facilitate the kind of introspection such a
>> thing would need?
> 
> Sort-of, mixed in with other literal values.
> -- 
> 	--Per Bothner
> per@bothner.com   http://per.bothner.com/

--
Jamison Hope
The PTR Group
www.theptrgroup.com



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

* Re: conditional bindings
  2014-12-11 22:43             ` Jamison Hope
@ 2014-12-11 23:16               ` Per Bothner
  2014-12-12  5:07               ` Per Bothner
  1 sibling, 0 replies; 10+ messages in thread
From: Per Bothner @ 2014-12-11 23:16 UTC (permalink / raw)
  To: kawa



On 12/11/2014 02:43 PM, Jamison Hope wrote:
> It just seems strange that the caller's environment affects whether the
> invokation matches a pattern with a literal.  I have trouble seeing how
>
> (let ((else #f)) (cond (#f (display "a\n")) (else (display "b\n"))))
> => #!void
>
> is hygienic, but
>
> (defmacro add (x y) `(+ ,x ,y))
> (let ((+ -)) (add 1 2))
> => -1
>
> is unhygienic.  In both cases, the behavior of the macro depends upon
> the *caller* environment and not just the lexical environment of the
> macro definition.

The former is hygienic because 'else' is both bound and referenced
in the application site; thus the reference matches the binding.
I.e. the else reference in the cond use is in the lexical scope
of the else definition that in scope in the cond application.

The add is unhygienic because '+ is referenced in the macro definition
and bound in the scope of the macro application.  I.e. the '+ in the add
definition is not in the lexical scope of the '+ in the macro application.
-- 
	--Per Bothner
per@bothner.com   http://per.bothner.com/

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

* Re: conditional bindings
  2014-12-11 22:43             ` Jamison Hope
  2014-12-11 23:16               ` Per Bothner
@ 2014-12-12  5:07               ` Per Bothner
  1 sibling, 0 replies; 10+ messages in thread
From: Per Bothner @ 2014-12-12  5:07 UTC (permalink / raw)
  To: kawa

On 12/11/2014 02:43 PM, Jamison Hope wrote:
> On Dec 11, 2014, at 1:36 PM, Per Bothner <per@bothner.com> wrote:
>>> (define (f #!key (x 0) (y 0)) (cons x y))
>>> (let ((y: 7) (x: 1)) (f y: 2 x: 3))
>>> => (3 . 2)
> ...
> With --r7rs it returns (0 . 0).

It is semi-correct: if x: and y: are plain symbols (rather than keywords)
then the x and y parameters both default to 0.

However, f should probably complain about excess/unused arguments.
That will hopefully happen with the apply-convention rewrite, which
will include faster and more robust keyword argument-passing.

> Actually, I can't figure out a way
> to pass a keyword argument with --r7rs, although it didn't prevent
> me from defining the function.

Kawa supports the Guile/Racket syntax:
   (f #:y 2 #:x 3) ==> (3 . 2)

I edited the Keywords section in doc/kawa.texi to better document
this syntax.  (It was only entioned in the "Hash-prefixed forms" section.)
-- 
	--Per Bothner
per@bothner.com   http://per.bothner.com/

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

end of thread, other threads:[~2014-12-12  5:07 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-12-09  5:48 conditional bindings Per Bothner
2014-12-09 21:16 ` Jamison Hope
2014-12-10  3:25   ` Per Bothner
2014-12-11  4:05     ` Jamison Hope
2014-12-11  4:38       ` Per Bothner
2014-12-11 16:43         ` Jamison Hope
2014-12-11 18:37           ` Per Bothner
2014-12-11 22:43             ` Jamison Hope
2014-12-11 23:16               ` Per Bothner
2014-12-12  5:07               ` Per Bothner

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