jerome.martin.dev
2018-3-23 12:35:34

Yeah, if I have some time I’ll see if I can put something together.


dmitryhertz
2018-3-23 13:30:33

Hello everyone!)


dmitryhertz
2018-3-23 13:32:18

Is it possible to disable hygiene in a macro definition? Everything looks like okay, expands okay but when I try to evaluate the entire module I get an error: “unbound identifier in module”.


dmitryhertz
2018-3-23 14:15:27

All the day I struggle and suffer. Using Common Lisp or Clojure I’d solve it long time ago. (


jerome.martin.dev
2018-3-23 14:18:26

@dmitryhertz You can use raco macro-stepper file.rkt and press the End >>\| button to get to your error message, then press \|< Step to go back step by step until your error disappears. This will show you the current state of the macro expander, allowing you to see what’s going wrong with your macro.


jerome.martin.dev
2018-3-23 14:19:05

It’s a bit hard to read at first, but you’ll soon get the grasp of it


jerome.martin.dev
2018-3-23 14:20:53

As for hygiene, it’s built-in in Racket and allows a very powerful (alas complex at first) macro system. But they are ways to break hygiene. If you want to inject identifiers in scope, for example (thus breaking lexical scope) the prefered way is to use syntax-parameterize


greg
2018-3-23 14:22:06

I mean, there exists defmacro but it’s a footgun and kittens will die if you use it https://docs.racket-lang.org/compatibility/defmacro.html


jerome.martin.dev
2018-3-23 14:22:24

A simple example is the aif macro (anaphoric if): https://github.com/jsmaniac/anaphoric/blob/master/aif.rkt


greg
2018-3-23 14:23:02

It’s better to follow @jerome.martin.dev’s advice and try to figure it out for real


dmitryhertz
2018-3-23 14:23:15

dmitryhertz
2018-3-23 14:24:41

Okay. I’ll try syntax-parameterize, @jerome.martin.dev thank you!


dmitryhertz
2018-3-23 14:25:52

@greg you made the great article! I mean “the fear of macros”. Thank you! :slightly_smiling_face:


jerome.martin.dev
2018-3-23 14:26:28

Yes, greg’s article is very helpful :smile:


jerome.martin.dev
2018-3-23 14:28:37

@dmitryhertz btw I think there’s an issue in your code, as you are using an unquote instead of a syntax-unquote here: #`(define (fn-id ld attr ...) ,req resp) I think it should be #,req, but maybe I’m wrong.


jerome.martin.dev
2018-3-23 14:29:41

Or maybe actually no unquoting at all, since req is a syntax variable.


dmitryhertz
2018-3-23 14:31:05

@jerome.martin.dev I already removed , from ,req, I added the comma there accidentally. Anyway, the error is: ; rdn: unbound identifier in module ; context...: ; #(2290265 module) #(2290266 module ldap 0) #(2292600 macro) #(2292909 local) ; #(2292910 intdef) #(2292913 local) #(2292914 intdef) #(2292917 local) ; #(2292918 intdef) #(2292921 local) #(2292922 intdef) #(2292925 local) ; #(2292926 intdef)


greg
2018-3-23 14:32:56

“unbound identifier in module” can happen when your macro supplies some identifier, but you didn’t require the module that provides it, where you defined the macro.


greg
2018-3-23 14:33:46

So e.g. this will give that error: #lang racket/base (module m racket/base (require (for-syntax racket/base syntax/parse)) (define-syntax (s stx) (syntax-parse stx [_ #'get-pure-port])) (provide s)) (require 'm) s


greg
2018-3-23 14:34:19

But this will work: #lang racket/base (module m racket/base (require (for-syntax racket/base syntax/parse)) (require net/url) ;; <============== NEW ======== (define-syntax (s stx) (syntax-parse stx [_ #'get-pure-port])) (provide s)) (require 'm) s


greg
2018-3-23 14:35:17

TL;DR: If your macro is supplying things like write-asn1/DER then in your macro-defining file be sure to require the modules that provide it


greg
2018-3-23 14:35:42

Not sure that’s the problem here, but it’s a mistake you can make


jerome.martin.dev
2018-3-23 14:35:49

in his case I think rdn stands for an attribute passed when using the macro, so I guess the macro actually compiles.


dmitryhertz
2018-3-23 14:42:59

Hmm… I realized that I cannot pass attr … to syntax-parameterize


dmitryhertz
2018-3-23 14:44:57

The problem is in attr … in that it can have different length in various the macro calls.


jerome.martin.dev
2018-3-23 14:45:40

yes, you can use syntax-class to simplify your macro. Wait a bit, I’m putting something together for you.


jerome.martin.dev
2018-3-23 14:52:26

the issue is that you’re putting parts of the syntax in a hash to then build up the result. But using syntax-class, you can remove the hash stuff and use req.asn1-type, req.app ..etc


dmitryhertz
2018-3-23 14:55:14

> you’re putting parts of the syntax in a hash It seemed to me that it’s the wrong technique, now I used it to be able to put keys and values in random order. Okay, I’ll try to rewrite it withous this hash. :slightly_smiling_face:


jerome.martin.dev
2018-3-23 15:00:39

This is a beginning (define-syntax (define-ldap stx) (define-splicing-syntax-class type (pattern (~seq #:asn1-type type:id)) (pattern (~seq) #:with type #'SomeDefaultValue)) (define-splicing-syntax-class req-expr (pattern (~seq t:type a:app))) (syntax-parse stx [(_ (name:id ld attr ...) [request re:req-expr ...] [response resp-expr]) ;; ...rest of the macro


jerome.martin.dev
2018-3-23 15:02:11

In short, you can declare every possible part of the request syntax, and put them together


jerome.martin.dev
2018-3-23 15:02:56

here I declare a type class which must be a sequence of two elements, #:asn1-type followed by an identifier called type


jerome.martin.dev
2018-3-23 15:03:54

it can also be an empty sequence (~seq), in which case type will be equal to SomeDefaultValue


jerome.martin.dev
2018-3-23 15:04:44

then I declare a req-expr class to be able to contain a sequence of a type class and an app class (not shown here)


jerome.martin.dev
2018-3-23 15:06:08

the code is not complete, and I gotta go, but you can check out the (~seq) documentation https://docs.racket-lang.org/syntax/stxparse-patterns.html?q=syntax%2Dparse#%28form._%28%28lib._syntax%2Fparse..rkt%29._~7eseq%29%29


jerome.martin.dev
2018-3-23 15:07:05

to use a class, just add it after a colon : on your macro identifiers


jerome.martin.dev
2018-3-23 15:07:37

for example (_ name:id age:int photo:my-photo-class)


jerome.martin.dev
2018-3-23 15:09:12

then you can use the class in your template with a dot notation: #'(define (student) (let ([my-name name] [my-age age]) (display photo.description) (display photo.image)))


dmitryhertz
2018-3-23 15:21:34

I tried to use define-splicing-syntax-class when I read asn1 library sources https://github.com/rmculpepper/asn1/blob/master/asn1-lib/main.rkt So, probably it’s worth to try to use it again. )


jerome.martin.dev
2018-3-23 15:28:52

my syntax-parse knowledge is fairly new, other people will surely correct me :wink: You need to find a (pattern) that allows a sequence of request “parts”. I don’t know if you need them in the right order or not.


dmitryhertz
2018-3-23 16:05:29

@jerome.martin.dev it nearly works!:) I’m about completing it, I hope soon it will be working fine


dmitryhertz
2018-3-23 16:31:37

A-a-a-a-a-a!!! It works!!! http://pasterack.org/pastes/61195


jerome.martin.dev
2018-3-23 16:47:44

Great :smile: I think you don’t need those extra syntax-parse though


dmitryhertz
2018-3-23 16:47:51

@jerome.martin.dev @greg thank you very much!:+1:


jerome.martin.dev
2018-3-23 16:50:02

You’re welcome :slightly_smiling_face:


dmitryhertz
2018-3-23 17:38:51

dmitryhertz
2018-3-23 17:40:10

So, now I use #,(stx-cadr #’req.n) instead of #,(syntax-parse #’req.n [(k:keyword n) #’n]) form. Maybe there’s something better, maybe I’ll fix it in the near future.


dmitryhertz
2018-3-23 17:40:39

Oh, it’s great to be able to develop some macros in Racket.


jerome.martin.dev
2018-3-23 17:41:18

I discovered syntax-parse and syntax-class some time ago. I can’t go back, it’s too good :smile:


jerome.martin.dev
2018-3-23 17:42:33

Yeah I don’t know yet how to do stuff like #'(hello my.super.deep.nested.value)


jerome.martin.dev
2018-3-23 17:42:47

I guess it’s not possible yet


jerome.martin.dev
2018-3-23 17:43:05

That would be cool though


dmitryhertz
2018-3-23 17:44:55

If I understood it right, @greg wrote about it here at 4.3 section: http://www.greghendershott.com/fear-of-macros/all.html


dmitryhertz
2018-3-23 17:45:45

I guess it’s possible to develop something like this


dmitryhertz
2018-3-23 17:46:50

But probably it won’t be easy for a racketeer who doesn’t have enough experience with Racket’s metaprogramming.


dmitryhertz
2018-3-23 17:47:56

For example, I spent 2–3 days to write define-ldap macro and I’d be struggling 2–3 days more without community’s great help


jerome.martin.dev
2018-3-23 17:49:13

Yes, the first time you try something with macros in Racket, it can be a real struggle. But when the frustration passes by, you realize you learned something valuable for the rest of your life x)


greg
2018-3-23 17:49:29

greg
2018-3-23 17:54:48

Sometime after I wrote that infix dot example, Racket added a reader option to produce #%dot forms. Which might be a better way to do this if you control the reader, e.g. for your own #lang. (But I guess maybe not for a generic macro?) http://docs.racket-lang.org/reference/reader.html?q=dot%20reader#%28part._parse-cdot%29


leif
2018-3-23 19:43:08

@mflatt If I eval a require statement for a relative module path, how would I tell Racket to require the file relative to a different directory.


mflatt
2018-3-23 19:44:15

By setting current-load-relative-directory, or by building up a more absolute module path with operations like module-path-index-join


leif
2018-3-23 19:46:52

Okay thanks. I also see current-load, and current-module-name-resolver, where do they fit in the picture?


leif
2018-3-23 19:47:53

It almost looks to me like current-load and current-module-name-resolver both use the current-load-relative-directory parameter, but I’m not sure how those two interplay, as the docs seem to imply they are both used for require forms.


michael.ballantyne
2018-3-23 19:55:41

michael.ballantyne
2018-3-23 19:57:39

@mflatt: I’m trying to construct and evaluate a module with syntax I load from a file, such that relative requires in that syntax will be relative to the file I loaded it from. I can’t just load from a path because I want to control the #lang independently from the file.


michael.ballantyne
2018-3-23 19:58:10

(This is for a test runner where I have several equivalent implementations of a language and want to run the same test file with each implementation)


michael.ballantyne
2018-3-23 19:59:05

Whoops: nevermind. Just realized the code I pasted didn’t have the current-load-relative-directory parameterize around the require that finally triggers evaluation.


michael.ballantyne
2018-3-23 19:59:08

With that it’s fixed.


michael.ballantyne
2018-3-23 19:59:52

Though if there’s a better way to do this, I’d be interested.


leif
2018-3-23 20:04:03

@mflatt @michael.ballantyne Is that actually right though?


leif
2018-3-23 20:04:34

Like, if I require a.rkt, which in tern requires b.rkt, I would expect b.rkt to be required relative to a.rkt, not the file that required a.rkt.


leif
2018-3-23 20:10:43

Oh interesting, if I just switch the (eval '(require 'a)) to (require "Desktop/a.rkt"), and then print out the load paths with:

(begin-for-syntax
  (define prev-load (current-load))
  (current-load
   (λ args
     (displayln args)
     (apply prev-load args))))

it does update the path properly.


leif
2018-3-23 22:13:26

Ah, okay. So if I make another c.rkt file that b.rkt requires, then you do get the right path. It seems like its just because the initial require is in an eval.