Repository navigation
define/curry - Defines an automatically currying procedure #5
Description
Activity
I'm not very experienced writing Racket or macros, so any improvement suggestions are welcome. 😊
- changed the title
[-]define/curry[/-][+]`define/curry` - Defines an automatically currying procedure[/+]on Jul 11, 2021 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
definethat can achieve this?(define ((insert-between2 mid) left right) (string-join (list left mid right) ""))
The docs show that the
headform 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")
Reacted by Ben Greenman and Lazerbeak12345Hi, 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
curryfunction'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 addfor-syntaxrequires.Annoying plumbing suggestion: use
syntax/locorquasisyntax/locso that errors for things like(insert-between 1 2 3 4)point to thedefine/curryline 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 errorOh, 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 numberMaybe 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-betweenis in a different module than the macro, I think the name will be even more confusing.Yeah you're right… I tried using
syntax/locbut the behavior was the same. Maybe I'm just not using it right though. I tried surrounding both thedefineand thecurryparts. 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.
Reacted by Ben Greenman and Lazerbeak12345Reacted by Ben Greenman- added 2 commits that reference this issue
on Oct 27, 2021
Macro
Defines an automatically currying procedure in a single step. Uses
curryinternally.(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 #,(syntax/loc stx (λ (np.params ...) body ...))))]))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.