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 [email protected].
For more options, visit https://groups.google.com/d/optout.

Attachment: monad.rkt
Description: Binary data

Reply via email to