Does racket allow for function overloading?
Asked Answered
F

2

6

I am new to Lisp-scheme and fairly new to the functional paradigm as a whole, and am currently doing an assignment which requires me to overload a function with the same name, but different sets of parameters in racket. Below is an example of what I'm trying to achieve:

#lang racket

(define (put-ball-in-box two-by-fours nails ball)
  ... )

(define (put-ball-in-box box ball)
  ... )

These are not the actual functions, but close enough. As implied, both functions would put a ball in a box, but one would assemble the box from its components first, then call the other. Obviously, when I try the above in DrRacket or using the command line, I get a module: duplicate definition for identifier ... error.

Is there a way to achieve this in racket?

Maybe the answer is right in front of me, but I have spent the last two hours searching for this and couldn't find anything, so would appreciate any pointers.

Thank you.

Frumpy answered 4/2, 2018 at 23:31 Comment(1)
It's possible to make your own version of define that allows that sort of thing. It's not a beginner project to make one though. For an example look at Formica: github.com/samsergey/formica/blob/master/README.mdFerroelectric
T
6

It doesn't in the usual sense of "writing another definition somewhere else."

It allows shadowing, which is defining a procedure with the same name as an imported procedure. Thus you can (define + ...) and your definition of + will hide the + from racket/base. If you want the original procedure, then you can do something like the following, where I define + to be either addition or string-appending.

 #lang racket/base
 (require (rename-in racket/base (+ base:+)))

 (define (+ . args)
   (if (andmap string? args)
       (apply string-append args)
       (apply base:+ args)))

Another thing you can do is use racket/match to have different behavior based on the shape of the argument.

#lang racket/base
(require racket/match)
(define (fib . arg)
  (match arg
   [(list n) (fib n 1 0)]
   [(list 1 a b) a]
   [(list 0 a b) b]
   [(list n a b) (fib (sub1 n) (+ a b) a)]))

This second example still doesn't quite do what you want since you have to go to the original definition point and modify the match clauses. But it might be sufficient for your purposes.

A more complicated example would be to use custom syntax to create a define/overload form. But I think you'll find the racket/match solution to be best.

Tartaglia answered 4/2, 2018 at 23:53 Comment(2)
Although I'd normally just write it out like you did, FWIW there are also some tiny sugar cubes like define/match, match-lambda, match-lambda*, etc. See docs.racket-lang.org/reference/…Lavinia
Also note that there's case-lambda which can be compiled more efficiently, but less versatile compared to match.Inn
N
4

You have the concept of default values as in JS and PHP:

(define (fib n (a 0) (b 1))
  (if (zero? n)
      a
      (fib (sub1 n) b (+ a b))))

(fib 10) ; ==> 55

Now if you had 5 optional parameters you need to order them and even pass some values just to be able to add a later one. To avoid that you can use keywords:

(define (test name #:nick [nick name] #:job [job "vacant"])
  (list name nick job))

(test "sylwester" #:job "programmer")
; ==> ("sylwester" "sylwester" "programmer")

Now Racket has classes. You can call a method like (send object method args ...).

(define circle%
  (class object%
    (super-new)
    (init-field radius)
    (define/public (area)
      (* radius radius 3.1415))))

(define cube%
  (class object%
    (super-new)
    (init-field side)
    (define/public (area)
      (* side side))))


(define circle (new circle% [radius 7]))
(define cube (new cube% [side 7]))

(map
 (lambda (o) (send o area))
 (list circle cube))
; ==> (153.9335 49)

Notice that the two classes hasn't really commited to a joint interface with area so this is pure duck typing. Thus you can make a function that expects a class that implements a message and it doesn't need to worry about other aspects of the class at all.

Nodical answered 5/2, 2018 at 0:46 Comment(2)
Thank you for your answer. I tested both yours and law-of-lives' one, and both actually would have worked, so since I cannot accept both, as per these guidelines I am going to upvote yours as it was also very helpful, and accept law-of-lives' one for posterity.Frumpy
@claudiusbrout of curiosity I've added OO.Nodical

© 2022 - 2024 — McMap. All rights reserved.