Specializing functions accepting keywords

27 views
Skip to first unread message

Jens Axel Søgaard

unread,
Jul 10, 2019, 3:25:25 PM7/10/19
to Racket list
Consider the following fishy example wherein we try to
reuse a function with keyword arguments.


(define (fish #:name    [name   #f]
              #:color   [color  #f]
              #:studio  [studio #f]
              #:eyes    [eyes   2])
  (~a "The fish " name " is " color ", appears in a movie from " studio " and has " eyes " eyes."))

Let's check see an example:

> (fish #:name "Nemo"   #:color "orange" #:studio "Disney")
"The fish Nemo is orange, appears in a movie from Disney and has 2 eyes."

A few examples later one quickly realizes that Disney has
a lot of fish in their movies:


(fish #:name "Nemo"   #:color "orange" #:studio "Disney")
(fish #:name "Dory"   #:color "blue"   #:studio "Disney")
(fish #:name "Marlin" #:color "orange" #:studio "Disney")
(fish #:name "Wanda"  #:color "gold"   #:studio "MGM")
(fish #:name "Blinky" #:color "orange" #:studio "Fox" #:eyes 3)


No worries, we can quickly define a  disney-fish  that simply
calls  fish  using the #:studio keyword and passes other keywords
along. The new  disney-fish must accept the same keywords as fish
(except for the studio one), so  procedure-reduce-keyword-arity
is needed to restrict the accepted keywords.


(define disney-fish
  (let ()
    (define (make-disney-fish kws kw-args . rest)
      (keyword-apply/sort fish kws kw-args rest #:studio "Disney"))

    (define-values (fish-required fish-allowed) (procedure-keywords fish))
    (define fish-arity (procedure-arity fish)) ; number of by-position arguments

    (procedure-reduce-keyword-arity
       (make-keyword-procedure make-disney-fish)
       fish-arity
       (remove '#:studio fish-required)  
       (remove '#:studio fish-allowed))))

Now we can write:

    (disney-fish #:name "Nemo" #:color "orange")

The new  disney-fish  accepts only keywords accepted by fish,
so any error reporting works as expected.

But ... the above solution wasn't quick - it took more work than I initially expected.
Am I missing something that makes reusing functions accepting
keyword arguments easier?

/Jens Axel


-- full example --


#lang racket

(require kw-utils/keyword-apply-sort)

(define (fish #:name    [name   #f]
              #:color   [color  #f]
              #:studio  [studio #f]
              #:eyes    [eyes   2])
  (~a "The fish " name " is " color ", appears in a movie from " studio " and has " eyes " eyes."))

(fish #:name "Nemo"   #:color "orange" #:studio "Disney")
(fish #:name "Dory"   #:color "blue"   #:studio "Disney")
(fish #:name "Marlin" #:color "orange" #:studio "Disney")
(fish #:name "Blinky" #:color "orange" #:studio "Fox" #:eyes 3)

(define disney-fish
  (let ()
    (define (make-disney-fish kws kw-args . rest)
      (keyword-apply/sort fish kws kw-args rest #:studio "Disney"))
    (define-values (fish-required fish-allowed) (procedure-keywords fish))
    (define fish-arity (procedure-arity fish)) ; number of by-position arguments
    (procedure-reduce-keyword-arity
     (make-keyword-procedure make-disney-fish)
     fish-arity
     (remove '#:studio fish-required)
     (remove '#:studio fish-allowed))))
   

(disney-fish #:name "Nemo" #:color "orange"
             #:studio "foo")

; Notes:
; > (disney-fish #:name "Nemo" #:color "orange" #:studio "Disney")
; correctly shows error (#:studio not expected)

Philip McGrath

unread,
Jul 11, 2019, 9:23:04 AM7/11/19
to Jens Axel Søgaard, Racket list
The easiest way I know is to use `curry`, since Alexis fixed its keyword argument support last year:

#lang racket


(define (fish #:name    [name   #f]
              #:color   [color  #f]
              #:studio  [studio #f]
              #:eyes    [eyes   2])
  (~a "The fish " name " is " color ", appears in a movie from " studio " and has " eyes " eyes."))

(define disney-fish
  (curry fish #:studio "Disney"))


(disney-fish #:name "Nemo" #:color "orange")

… but it might be useful to have some other library functions for this sort of thing: maybe a "curry once" or something like the `adjust-keyword-default` used in the `#lang slideshow` implementation.

-Philip


--
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...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/CABefVgyXzWW1WJjC9SViyzsheswJD9Ek4LDfwqaNg_v4mT6y%3DQ%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages