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: def-monad.rkt
Description: Binary data

Reply via email to