Open agj opened 3 years ago
I'm not very experienced writing Racket or macros, so any improvement suggestions are welcome. 😊
I'm not very experienced writing Racket or macros, so any improvement suggestions are welcome. 😊
This is a great little macro! Did you know, however, that there's another form for define
that can achieve this?
(define ((insert-between2 mid) left right)
(string-join (list left mid right) ""))
The docs show that the head
form in the grammar is recursive :)
Hi, Rotor! Thanks! Yes, I'm aware of that syntax. However as far as I understand it, that syntax doesn't allow you to do something like this. All of the following are equivalent:
(insert-between "-" "left" "right")
((insert-between "-" "left") "right")
((insert-between "-") "left" "right")
((insert-between) "-" "left" "right")
Hi, Rotor! Thanks! Yes, I'm aware of that syntax. However as far as I understand it, that syntax doesn't allow you to do something like this. All of the following are equivalent:
(insert-between "-" "left" "right") ((insert-between "-" "left") "right") ((insert-between "-") "left" "right") ((insert-between) "-" "left" "right")
Oh wow you're totally right! Perhaps you should add some of those examples to your original post!
@Fictitious-Rotor I updated with a link to the curry
function's docs so that it doesn't look like my macro is doing any magic, and extended the example code a tiny bit. 👍
Looks great!
Small suggestion: move the syntax class to the toplevel. It'll have to go inside a (begin-for-syntax ....)
and you will probably need to add for-syntax
requires.
Annoying plumbing suggestion: use syntax/loc
or quasisyntax/loc
so that errors for things like (insert-between 1 2 3 4)
point to the define/curry
line instead of pointing to the lambda in the macro body.
Hi, @bennn! Thanks! I tried your suggestions. As for syntax/loc
(which I didn't know about before, so that was cool to learn), I didn't notice any change adding it, and without it it seems to behave well, so I guess this is a case in which it's not needed? Like this:
(require (for-syntax syntax/parse))
(begin-for-syntax
(define-syntax-class name-params
#:description "name and parameters clause"
(pattern (name:id params:id ...+)
#:fail-when (check-duplicate-identifier
(syntax->list #'(params ...)))
"duplicate parameter name")))
(define-syntax (define/curry stx)
(syntax-parse stx
[(_ np:name-params body ...+)
#'(define np.name
(curry (λ (np.params ...)
body ...)))]))
(define/curry (insert-between mid left right)
(string-join (list left mid right) ""))
(insert-between 2 3 3 4 4 44) ; This line gets highlighted with an error
Oh, that's great that the line with the function call gets highlighted.
When I run on the command line, though, it prints the function and says that function is defined on line 17 (inside the define-syntax
)
curried:...test.rkt:17:16: arity mismatch;
the expected number of arguments does not match the given number
Maybe a better way to phrase it is: get (displayln insert-between)
and (displayln (object-name insert-between))
to print the right line.
If the definition of insert-between
is in a different module than the macro, I think the name will be even more confusing.
Yeah you're right… I tried using syntax/loc
but the behavior was the same. Maybe I'm just not using it right though. I tried surrounding both the define
and the curry
parts. I'll try again later using those functions, as it does seem like a useful thing in general to know for API ergonomics.
Hint: the λ
is what needs a loc
@bennn Sorry for taking so long! I got a bit busy and distracted, but I tried your hint just now and it worked great! Thanks for the tip.
Macro
Defines an automatically currying procedure in a single step. Uses
curry
)) internally.Example
Before and After
This code-cleaning macro reduces some boilerplate necessary to define a curried function. Code like this:
Is simplified to this, identical in form to defining a regular procedure:
I wrote an earlier version of this macro when implementing some Haskell code in Racket, and thought it was useful enough to share!
Licence
I release the above code under the MIT license, and accompanying text under the Creative Commons Attribution 4.0 International license.