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 fix
> the unbound definitions from `define-trans' sites (or would it?).
> 
> What's the most hygienic solution that makes this dirty macro work?
> Any pointer in the right direction would be appreciated.
> 
> Cheers,
> Nick

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Attachment: monad.rkt
Description: Binary data

Reply via email to