Search code examples
lambdaschemeracketvariadicarity

Function in Scheme / Racket returning functions of particular arities


How to define a function in Scheme / Racket that returns functions of particular arities?

Currently I have in my code the following:

(define (get-function n)
  (cond
    [(= n 1) (lambda (a) a)]
    [(= n 2) (lambda (a b) (+ a b))]
    [(= n 3) (lambda (a b c) (+ a b c))]
    ; and so on
    ))

Of course arity of a function it returns is n:

(procedure-arity (get-function 3)) ; returns 3

Please don't mind "+", in my program it's more complicated than a fold of "+". However, the structure of that function could be defined recursively as well; similar to:

(define (get-function-variadic n)
  (lambda arguments
    (if (empty? arguments) 0
        (+ (car arguments)
           (apply (get-function-variadic (sub1 n)) (cdr arguments))))))

But then it returns variadic functions:

(procedure-arity (get-function-variadic 3)) ; returns #(struct:arity-at-least 0)

so that all of these work just as the normal Scheme's variadic "+":

((get-function-variadic 3) 1 2 3)

((get-function-variadic 3) 1 2)
((get-function-variadic 3) 1 2 3 4)

In fact I want only the first one to return a result, while others should return an error "wrong number of arguments". Additionally, in other parts of a program I rely on arity of the functions generated. So, a variadic function doesn't suit me (even if it checks the length of "arguments"). I need a set of functions of different integer arities returned by (get-function n). Is it possible to define in Scheme / Racket?


Solution

  • This feels like an XY problem, so I don't know if this well help you, but:

    As @Renzo commented, if you do not need to do this at run time, it might be cleaner and faster to use a macro to do it at compile time.

    I don't understand why you need a get-function-variadic that returns functions that are... not variadic. However I suppose you could use procedure-reduce-arity to get the expected results in your examples:

    #lang racket
    
    (define (-get-function-variadic n)
      (lambda arguments
        (if (empty? arguments) 0
            (+ (car arguments)
               (apply (get-function-variadic (sub1 n)) (cdr arguments))))))
    
    (define (get-function-variadic n)
      (procedure-reduce-arity (-get-function-variadic n) n))
    
    
    (require rackunit)
    
    (check-exn exn:fail:contract:arity? (λ () ((get-function-variadic 3) 1 2)))
    (check-equal? ((get-function-variadic 3) 1 2 3) 6)
    (check-exn exn:fail:contract:arity? (λ () ((get-function-variadic 3) 1 2 3 4)))