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