I want to be able to do something like this:
(struct point (x y))
(define p1 (point 1 2))
(define p2 (point 10 20))
(+ p1 p2) ; -> (point 11 22)
Is it possible to teach a struct like point
to work with built-in math operators like +
?
The docs seem to manage to implement custom (equal? ...)
handling in section 5.5 on this page. What I'm trying to do is quite similar ...
Or should I just define function like (point-add p1 p2)
?
You can either
point-add
Use your own +
that matches against all possible value types that you want to take on. This is sufficient if you know all possible value types beforehand, but it wouldn't be easy to extend it to include newly created struct definitions in client's code. For example:
;; We will "shadow" Racket's + with our own +, but we still
;; need the functionality of Racket's +, so let's require
;; Racket's + but use the name racket:+ instead
(require (only-in racket/base [+ racket:+]))
(struct point (x y) #:transparent)
(define (+ x y)
(match* (x y)
[((point a b) (point c d)) (point (+ a c) (+ b d))]
[((point _ _) _) (error '+ "Can't add a point with non point")]
[(_ (point _ _)) (error '+ "Can't add a point with non point")]
[(_ _) (racket:+ x y)]))
;; in client's code
(+ (point 1 2) (point 3 4)) ;=> (point 4 6)
(+ 1 2) ;=> 3
Define a new generics so that we can do something similar to gen:equal+hash
for equal?
. For example:
(require racket/generic
(only-in racket/base [+ racket:+]))
(define-generics addable
(add addable _)
#:fast-defaults ([number?
(define (add x y) (racket:+ x y))]))
(define + add)
;; in client's code
(struct point (x y)
#:transparent
#:methods gen:addable
[(define (add x y)
(match* (x y)
[((point a b) (point c d)) (point (+ a c) (+ b d))]
[(_ _) (error 'add "Can't add a point with non point")]))])
(struct point-3d (x y z)
#:transparent
#:methods gen:addable
[(define (add x y)
(match* (x y)
[((point-3d a b c) (point-3d d e f))
(point-3d (+ a d) (+ b e) (+ c f))]
[(_ _) (error '+ "Can't add a point-3d with non point-3d")]))])
(+ (point 1 2) (point 3 4)) ;=> (point 4 6)
(+ (point-3d 1 2 3) (point-3d 4 5 6)) ;=> (point-3d 5 7 9)
(+ 1 2) ;=> 3
To accept multiple arguments, modify (3) as follows
(define +
(case-lambda
[() 0]
[(x . xs) (foldl add x xs)]))
;; client's code
(+ (point 1 2) (point 3 4) (point 5 6)) ;=> (point 9 12)
(+ 1 2 3) ;=> 6
(+) ;=> 0
(+ 1) ;=> 1
(+ (point-3d 1 2 3)) ;=> (point-3d 1 2 3)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With