Reputation: 227
New to Racket. I've been trying to make a simple vector struct and define common vector functions (dot product, norm...). I am aware of math/matrix but do not wish to use it for now.
Everything's going great until I tried to create a "minus function" to subtract vectors. The addition function works fine, but the subtraction raises typecheck errors. Here's the code :
#lang typed/racket
(struct: vect ([x : Real]
[y : Real])
#:transparent)
(: vect+ (-> vect * vect))
(define (vect+ . vs)
(vect (apply + (map vect-x vs))
(apply + (map vect-y vs))))
(: vect- (-> vect * vect))
(define (vect- . vs)
(vect (apply - (map vect-x vs))
(apply - (map vect-y vs))))
(vect+ (vect 1 2) (vect -3 1) (vect 0 4))
(vect- (vect 1 2) (vect -3 1) (vect 0 4))
The vect+ (vector addition) function works fine, but the vect- function raises :
. Type Checker: Bad arguments to function in `apply':
Domains: Number Number *
Arguments: (Listof Real) *
in: (apply - (map vect-x vs))
. Type Checker: Bad arguments to function in `apply':
Domains: Number Number *
Arguments: (Listof Real) *
in: (apply - (map vect-y vs))
. Type Checker: Summary: 2 errors encountered in:
(apply - (map vect-x vs))
(apply - (map vect-y vs))
Why would "minus" refuse reals when "plus" takes them and the following line works fine ?
(apply - (list 1 2 3))
; -> -4
Upvotes: 2
Views: 315
Reputation: 8373
(Building off of stchang's answer)
It works if you change the definition of vect-
to this:
(: vect- (-> vect vect * vect))
(define (vect- v . vs)
(vect (apply - (vect-x v) (map vect-x vs))
(apply - (vect-y v) (map vect-y vs))))
This is exactly because -
doesn't take zero arguments. Because of that, apply
needs some reassurance that there will be at least one argument.
Upvotes: 2
Reputation: 2540
The error is because apply
expects its first argument to be a function that accepts zero-or-more arguments, but -
requires one-or-more arguments (note that +
works because it accepts zero arguments).
> (:print-type apply)
(All (a b) (-> (-> a * b) (Listof a) b))
> (:print-type -)
; big output omitted,
; but notice it includes (-> Number Number * Number),
; but not (-> Number * Number))
> (-)
. Type Checker: could not apply function;
wrong number of arguments provided
expected at least: 1
given: 0 in: (-)
> (+)
- : Integer [more precisely: Zero]
0
To get around this, you can cast
apply
to a function that accepts a function of one-or-more as its first argument:
(: vect- (-> vect vect * vect))
(define (vect- . vs)
(define apply1+
(cast apply (All (a b) (-> (-> a a * b) (Listof a) b))))
(vect (apply1+ - (map vect-x vs))
(apply1+ - (map vect-y vs))))
(vect- (vect 1 2) (vect -3 1) (vect 0 4)) ; (vect 4 -3)
Make sure to update the type of vect-
accordingly.
OR, you can define an alternative subtraction function that accepts zero arguments:
(: new- (-> Real * Real))
(define (new- . args)
(if (empty? args)
0
(apply - args)))
(: vect- (-> vect * vect))
(define (vect- . vs)
(vect (apply new- (map vect-x vs))
(apply new- (map vect-y vs))))
Here, it's ok to give vect-
zero arguments.
Upvotes: 2
Reputation: 227
My guess is subtraction works differently since :
#lang racket
(- 5) ; -> -5
(- 5 3) ; -> 2
So I defined a neg function which gives the opposite of a vector and defined a syntax for vect-. Pretty sure it's an overkill but don't I don't know how to do it more simply. So adding this to the code works :
(: neg (-> vect vect))
(define (neg v)
(vect (- (vect-x v))
(- (vect-y v))))
(define-syntax vect-
(syntax-rules ()
[(vect- v) (neg v)]
[(vect- v1 v2 ...) (apply vect+ (list* v1 (map neg (list v2 ...))))]))
... but I'm pretty sure there's a MUCH better way.
Upvotes: 1