[racket-users] Re: As clean as possible in a dirty macro

2016-04-27 Thread Nicholas Labich
Thanks for the feedback. Attached is the solution applied to my original 
implementation (only 65loc, documented). A couple of interesting points:

- My simplified example didn't show this, but I have to
  `syntax-local-introduce' the identifiers recursively, else
  references to symbol=? `let-syntax'-bound identifiers are
  ambiguous. Each `returnₘ' reference can have at most one other
  `returnₘ' identifier in scope.
- For the `syntax-local-introduce'd identifiers to be visible in the
  body of the `splicing-let-syntax' [line 66], recursive calls to
  `use-monad' have to be `local-expand'ed by hand. I suppose I could
  recur over the stack from the top down, but I would need to
  special-case the first iteration after MId deference, which seems
  sloppier.

If there are any fixes for this second point, I'd be interested to hear. For 
now, this should be a good starting point.

Nick

On Monday, April 25, 2016 at 6:18:41 PM UTC-4, Nicholas Labich wrote:
> I'm trying to write a small monad transformer library with macros.
> 
> My goal:
> 
>   (define-monad IdM
> [(return a) a]
> [(bind m f) (f m)]
> [(foo)  'id-foo])
> 
>   (define-trans ListT
> [(return a) (returnₘ (list a))]
> [(bind m f) (bindₘ m (λ (loa) (foldl (λ (a m′) (returnₘ (append (f a) 
> m′)))
>  '()
>  loa)))]
> [(foo)  (cons 'list-foo (fooₘ))])
> 
>   (let ([ladd1 (λ (n) (list (add1 n)))])
> (use-monad ListT IdM)
> (displayln (foo))
> (equal? (bind (return 0) ladd1) (list 1)))
>   ; =>
>   ; (list-foo . id-foo)
>   ; #t
> 
> The problem:
> 
> I want to delay binding of returnₘ, bindₘ, and fooₘ (in general,
> arbitrary lifted effects) until `use-monad' expansion. I also want to
> save as much lexical context from the define-{monad,trans} sites as
> possible. Each `identₘ' should refer to the
> `splicing-let-syntax'-bound macros of the monad directly below that
> transformer in the stack during `use-monad' expansion (see below for
> an example expanded result).
> 
> My implementation:
> 
> Start at the bottom (right side) of the stack given to `use-monad',
> recursively generating `splicing-let-syntax' bindings for the
> ops/effects of each layer of the transformer stack. When at the top,
> generate `define-syntax' bindings with identifiers `format-id'ed to
> be exposed at the `use-monad' call-site.
> 
> The attached file has the whole implementation, but the gist of it is
> this (assuming the above definitions of ListT and IdM):
> 
>   (use-monad ListT IdM)
>   ; =>
>   (splicing-let-syntax
>   ([returnₘ (syntax-id-rules ()
>   [(returnₘ a) a]
>   [returnₘ (λ (a) a)])]
>[bindₘ   (syntax-id-rules ()
>   [(bindₘ m f) (f m)]
>   [bindₘ (λ (m f) (f m))])]
>[fooₘ(syntax-id-rules ()
>   [(fooₘ) 'id-foo]
>   [fooₘ (λ () 'id-foo)])])
> (begin
>   (define-syntax return
> (syntax-id-rules ()
>   [(return a) (returnₘ (list a))]
>   [return (λ (a) (returnₘ (list a)))]))
>   (define-syntax bind
> (syntax-id-rules ()
>   [(bind m f)
>(bindₘ m (λ (loa) (foldl (λ (a m′) (returnₘ (append (f a) m′)))
> '() loa)))]
>   [bind
>(λ (m f)
>  (bindₘ m (λ (loa) (foldl (λ (a m′) (returnₘ (append (f a) m′)))
>   '() loa]))
>   (define-syntax foo
> (syntax-id-rules ()
>   [(foo) (cons 'list-foo (fooₘ))]
>   [foo   (λ () (cons 'list-foo (fooₘ)))]
> 
> As implemented, all of the `identₘ'-form identifiers are unbound when
> `return', `bind', and `foo' are called.
> 
> Possible/attempted solutions:
> 
> If I take the entire result of `use-monad' and fully replace its
> context:
> 
>   (datum->syntax stx (syntax->datum (syntax-parse stx ...)))
> 
> the test "succeeds", but only by throwing away all context from the
> define-{monad,trans} sites. Is there a way to add macro bindings for
> the constructed `identₘ' transformers to the local context of the
> final definitions without throwing away the rest (or just expand them
> away during `use-monad's expansion)?
> 
> I've spent a few days trying to work with the `local-expand' variants,
> but have yet to find the proper invocation/ritual-sacrifice to expand
> the macros as I construct them in `use-monad' (the documentation is
> nigh-impenetrable, at least to this uninitiated).
> 
> I don't think syntax-parameters are the right solution, because while
> the effects that need to be lifted (and bound as `identₘ') will all
> be known statically, name collisions would prevent definitions at
> `define-trans' sites (since all transformers would define `returnₘ'
> and `bindₘ' parameters). If syntax parameters are to be defined and
> `(let () ...)'-guarded at each `use-monad' site, it wouldn't 

[racket-users] Re: As clean as possible in a dirty macro

2016-04-26 Thread Nicholas Labich
Hmm, no takers on this wall of text? ;)

Turns out I was looking in the wrong direction. While reading some of Matthew's 
sets of scopes notes, I stumbled across an example with exactly the tool I 
needed: `syntax-local-introduce'. Each of the intermediary `let-syntax'-bound 
macros just needed its identifier introduced in-context.

A more minimal example of the working code is below. Now that this is solved, 
is there a better way? Stacking units up was a bit too brittle in my pre-macro 
attempts.

#lang racket
(require (for-syntax syntax/parse racket/syntax)
 racket/splicing)

(define-syntax-rule (save-bind name id val)
  (define-syntax name (syntax [id val])))

(save-bind FOO foo 1)
(splicing-let ([bar-local 2])
  (save-bind BAR bar (+ bar-local foo)))
(splicing-let ([baz-local 3])
  (save-bind BAZ baz (+ baz-local bar)))

(define-syntax (dyn-bind stx)
  (syntax-parse stx

[(dyn-bind before ... name:id after ...)
 #`(dyn-bind before ... #,(syntax-local-value #'name) after ...)]

[(dyn-bind [id:id val:expr] after ...+)
 #`(splicing-let-syntax
   ([#,(syntax-local-introduce (datum->syntax #f (syntax->datum #'id)))
 (make-set!-transformer
  (λ (stx) (syntax-case stx ()
 [id (identifier? #'id) #'val])))])
 (dyn-bind after ...))]

[(dyn-bind [id:id val:expr])
 #`(define-syntax #,(format-id #'dyn-bind "~a" (syntax-e #'id))
 (make-set!-transformer
  (λ (stx) (syntax-case stx ()
 [id (identifier? #'id) #'val]]))

(splicing-let ([bar-local 500]
   [baz-local 1000])
  (dyn-bind FOO BAR BAZ))

(equal? baz 6)

Nick

On Monday, April 25, 2016 at 6:18:41 PM UTC-4, Nicholas Labich wrote:
> I'm trying to write a small monad transformer library with macros.
> 
> My goal:
> 
>   (define-monad IdM
> [(return a) a]
> [(bind m f) (f m)]
> [(foo)  'id-foo])
> 
>   (define-trans ListT
> [(return a) (returnₘ (list a))]
> [(bind m f) (bindₘ m (λ (loa) (foldl (λ (a m′) (returnₘ (append (f a) 
> m′)))
>  '()
>  loa)))]
> [(foo)  (cons 'list-foo (fooₘ))])
> 
>   (let ([ladd1 (λ (n) (list (add1 n)))])
> (use-monad ListT IdM)
> (displayln (foo))
> (equal? (bind (return 0) ladd1) (list 1)))
>   ; =>
>   ; (list-foo . id-foo)
>   ; #t
> 
> The problem:
> 
> I want to delay binding of returnₘ, bindₘ, and fooₘ (in general,
> arbitrary lifted effects) until `use-monad' expansion. I also want to
> save as much lexical context from the define-{monad,trans} sites as
> possible. Each `identₘ' should refer to the
> `splicing-let-syntax'-bound macros of the monad directly below that
> transformer in the stack during `use-monad' expansion (see below for
> an example expanded result).
> 
> My implementation:
> 
> Start at the bottom (right side) of the stack given to `use-monad',
> recursively generating `splicing-let-syntax' bindings for the
> ops/effects of each layer of the transformer stack. When at the top,
> generate `define-syntax' bindings with identifiers `format-id'ed to
> be exposed at the `use-monad' call-site.
> 
> The attached file has the whole implementation, but the gist of it is
> this (assuming the above definitions of ListT and IdM):
> 
>   (use-monad ListT IdM)
>   ; =>
>   (splicing-let-syntax
>   ([returnₘ (syntax-id-rules ()
>   [(returnₘ a) a]
>   [returnₘ (λ (a) a)])]
>[bindₘ   (syntax-id-rules ()
>   [(bindₘ m f) (f m)]
>   [bindₘ (λ (m f) (f m))])]
>[fooₘ(syntax-id-rules ()
>   [(fooₘ) 'id-foo]
>   [fooₘ (λ () 'id-foo)])])
> (begin
>   (define-syntax return
> (syntax-id-rules ()
>   [(return a) (returnₘ (list a))]
>   [return (λ (a) (returnₘ (list a)))]))
>   (define-syntax bind
> (syntax-id-rules ()
>   [(bind m f)
>(bindₘ m (λ (loa) (foldl (λ (a m′) (returnₘ (append (f a) m′)))
> '() loa)))]
>   [bind
>(λ (m f)
>  (bindₘ m (λ (loa) (foldl (λ (a m′) (returnₘ (append (f a) m′)))
>   '() loa]))
>   (define-syntax foo
> (syntax-id-rules ()
>   [(foo) (cons 'list-foo (fooₘ))]
>   [foo   (λ () (cons 'list-foo (fooₘ)))]
> 
> As implemented, all of the `identₘ'-form identifiers are unbound when
> `return', `bind', and `foo' are called.
> 
> Possible/attempted solutions:
> 
> If I take the entire result of `use-monad' and fully replace its
> context:
> 
>   (datum->syntax stx (syntax->datum (syntax-parse stx ...)))
> 
> the test "succeeds", but only by throwing away all context from the
> define-{monad,trans} sites. Is there a way to add macro bindings for
> the constructed `identₘ' transformers to the local context of the
> final