1

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?

formalizm
  • 47
  • 4
  • 3
    This is a typical task of a [macro](http://docs.racket-lang.org/guide/macros.html), not a function. – Renzo Sep 08 '15 at 08:34
  • @Renzo, I gave it a try via macros, see below. Seems to be working. However, there is a better option as suggested by Greg. – formalizm Sep 08 '15 at 19:54

2 Answers2

3

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)))
Greg Hendershott
  • 16,100
  • 6
  • 36
  • 53
  • 1
    Thanks, @Greg, it's exactly what I needed. Somehow I missed this procedure-reduce-arity in the documentation. And as regards naming, I called it "variadic" because it is variadic, and I couldn't make it non-variadic. :) – formalizm Sep 08 '15 at 19:21
  • I gave it a try via macros, see below. Seems to be working. However, your solution is clearly better. – formalizm Sep 08 '15 at 19:54
0

And here is a solution via macros. I devised it while still not knowing about (procedure-reduce-arity) function (which is more generic than this solution).

(define-syntax-rule (variadic->fixed-arity f arguments ...)
  (lambda (arguments ...) (f arguments ...)))

(define-for-syntax tails (lambda (l) (if (null? l) (list l) (cons l (tails (cdr l))))))

(define-for-syntax n 10)

(define-syntax (variadic->fixed-arities-up-to-n stx)
  (syntax-case stx ()
    [(variadic->fixed-arities-up-to-n f)
     (let* ([arguments (generate-temporaries (build-list n values))]
            [arguments-sets (reverse (tails arguments))])
       #`(list
          #,@(map (lambda (arguments-set)
                    #`(variadic->fixed-arity f #,@arguments-set))
                  arguments-sets)))]))

(define (get-function-of-arity f n)
  (define functions-of-fixed-arities (variadic->fixed-arities-up-to-n f))
  (if (n . >= . (length functions-of-fixed-arities))
      (error "increase number of generated fixed-arities functions")
      (list-ref functions-of-fixed-arities n)))

(procedure-arity (get-function-of-arity + 7)) ; returns 7
(apply (get-function-of-arity + 7) (make-list 7 3)) ; returns 21
formalizm
  • 47
  • 4