To make your vector+ operation take an arbitrary number of arguments, this 
is what I'd do:

(define (vector+ . vectors)
  (vector
    (for/sum ([v (in-list vectors)]) (vector-i v))
    (for/sum ([v (in-list vectors)]) (vector-j v))
    (for/sum ([v (in-list vectors)]) (vector-k v))))

(module+ test
  (test-case "vector+"
    (check-equal? (vector+) (vector 0 0 0))
    (check-equal? (vector+ (vector 1 2 3)) (vector 1 2 3))
    (check-equal? (vector+ (vector 1 2 3) (vector 4 5 6)) (vector 5 7 9))))

I can see some value in abstracting over this, if you've got a *lot* of 
structs with mathy variable-arity operations on them. I personally might 
try to do something with reducers 
<https://docs.racket-lang.org/rebellion/Reducers.html>. I'm not fully sure 
how I'd go about it though. Rather than experiment with that I'd probably 
just prefer to write the boring boilerplate code.
On Monday, February 1, 2021 at 6:26:20 PM UTC-8 making-a-racket wrote:

> Turns out that I don't see a way to calculate the #:auto-value using the 
> constructor fields, so I don't see how to make something like
>
> (struct test (x y z [data #:auto])
>   #:auto-value #(x y z))
>
> work.
>
> So I've decided to go with something like this:
>
> (struct color (r g b) #:transparent)
> (struct vector (i j k) #:transparent)
> (struct point (x y z) #:transparent)
>
> (define/contract (tuple-map-elementwise proc tuple)
>   (-> (-> real? real?) (or/c vector? point? color?) (or/c vector? point? 
> color?))
>   (match tuple
>     [(struct vector (i j k)) (vector (proc (vector-i tuple))
>                                      (proc (vector-j tuple))
>                                      (proc (vector-k tuple)))]
>     [(struct point (x y z)) (point (proc (point-x tuple))
>                                    (proc (point-y tuple))
>                                    (proc (point-z tuple)))]
>     [(struct color (r g b)) (color (proc (color-r tuple))
>                                    (proc (color-g tuple))
>                                    (proc (color-b tuple)))]))
>
> (define/contract (tuple-map-pairwise proc tuple1 tuple2)
>   (-> (-> real? real? real?) (or/c vector? point? color?) (or/c vector? 
> point? color?) (or/c vector? point? color?))
>   (match (list tuple1 tuple2)
>     [(list (struct vector (i1 j1 k1)) (struct vector (i2 j2 k2)))
>      (vector (proc (vector-i tuple1) (vector-i tuple2))
>              (proc (vector-j tuple1) (vector-j tuple2))
>              (proc (vector-k tuple1) (vector-k tuple2)))]
>     [(list (struct point (x1 y1 z1)) (struct point (x2 y2 z2)))
>      (point (proc (point-x tuple1) (point-x tuple2))
>             (proc (point-y tuple1) (point-y tuple2))
>             (proc (point-z tuple1) (point-z tuple2)))]
>     [(list (struct color (r1 g1 b1)) (struct color (r2 g2 b2)))
>      (color (proc (color-r tuple1) (color-r tuple2))
>             (proc (color-g tuple1) (color-g tuple2))
>             (proc (color-b tuple1) (color-b tuple2)))]))
>
> (define (tuple-op-by-constant op tuple c)
>   (tuple-map-elementwise (λ (e) (op e c)) tuple))
>
> (define (tuple-op-pairwise op t1 t2 . tuples)
>   (cond [(empty? tuples) (tuple-map-pairwise op t1 t2)]
>         [else (apply tuple-op-pairwise op
>                      (tuple-map-pairwise op t1 t2)
>                      (car tuples)
>                      (cdr tuples))]))
>
> (define (vector+c v c)
>   (tuple-op-by-constant + v c))
>
> (define (vector*c v c)
>   (tuple-op-by-constant * v c))
>
> (define (vector+ u v . vectors)
>   (apply tuple-op-pairwise + u v vectors))
>
> (define (vector- u v . vectors)
>   (apply tuple-op-pairwise - u v vectors))
>
> Then I'll do something similar for points and colors. This library will 
> probably be converted to Typed Racket, so the contracts are only temporary.
>
> This seems to be the cleanest solution I think, and it's actually quite 
> similar to the F# code I've written for the same project.
>
> I know this seems simple, but wanted to check on people's thoughts on 
> idiomatic ways in Racket, and to make sure I wasn't missing some secret 
> sauce of structs or something else.
>
>
> On Monday, February 1, 2021 at 4:38:37 PM UTC-6 making-a-racket wrote:
>
>> Apologies. By fourth optional argument, I meant a fourth field with the 
>> #auto field option. I'm experimenting with this now.
>>
>> On Monday, February 1, 2021 at 3:24:49 PM UTC-6 making-a-racket wrote:
>>
>>> Thanks for the suggestion and for the macro implementation. I'll have to 
>>> pour over that a bit.
>>>
>>> I wanted to do map because I wanted to make it easy to idiomatically 
>>> implement addition and other such operators on my data types such that they 
>>> accept arbitrary amounts of arguments and provide the map for other uses.
>>>
>>> So that's why the vector (in the Racket sense) is the most simple option 
>>> in that respect, since I can trivially do:
>>>
>>> (define (sum-vector v)
>>>     (apply + (vector->list v)))
>>>
>>> (define (vector+ . vs)
>>>     (apply vector-map + vs)) 
>>>
>>> (define (vector-i v)
>>>     (vector-ref v 0))
>>> ;; and so on
>>>
>>> The only think I don't get there are my wanted datatypes and associated 
>>> predicates, since vectors, points, and colors would all be Racket vectors.
>>>
>>> I could almost do structs with a fourth optional argument that holds a 
>>> Racket vector that never gets used explicitly by the "user" and build 
>>> helper functions to properly update it, which is then used to build all the 
>>> operator and other such functions.
>>>
>>> If I just do structs as I originally notated, how do you suggest I 
>>> implement things like vector+ to take in arbitrary amounts of arguments?
>>>  
>>>
>>> On Monday, February 1, 2021 at 12:48:19 AM UTC-6 [email protected] 
>>> wrote:
>>>
>>>> I'd suggest just going with the structs and making them transparent. 
>>>> It's only three structs and only with a handful of fields, abstracting 
>>>> over 
>>>> them with map and fold doesn't seem worth the added complexity IMO. But if 
>>>> you'd really like to map and fold over structs, I highly recommend using 
>>>> macros, `syntax-parse` and the struct-id 
>>>> <https://docs.racket-lang.org/syntax-classes/index.html?q=struct-id#%28form._%28%28lib._syntax%2Fparse%2Fclass%2Fstruct-id..rkt%29._struct-id%29%29>
>>>>  
>>>> syntax class to do so:
>>>>
>>>> (require (for-syntax syntax/parse/class/struct-id)
>>>>          syntax/parse/define)
>>>>
>>>> (define-simple-macro (struct-map type:struct-id instance-expr:expr 
>>>> map-function-expr:expr)
>>>>   (let ([map-function map-function-expr]
>>>>         [instance instance-expr])
>>>>     (type.constructor-id (map-function (type.accessor-id instance)) 
>>>> ...)))
>>>>
>>>> (struct point (x y z) #:transparent)
>>>>
>>>> ;; Outputs (point 2 3 4)
>>>> (struct-map point (point 1 2 3) add1)
>>>> On Sunday, January 31, 2021 at 4:20:03 PM UTC-8 making-a-racket wrote:
>>>>
>>>>> Hello. I have a project where I am needing to represent vectors (in 
>>>>> the mathematical sense), points, and colors. Both the vectors and points 
>>>>> will be 3D. I'm having trouble knowing what's an idiomatic way to 
>>>>> represent 
>>>>> and interact with data types that are similar but different.
>>>>>
>>>>> In general, I could simply all represent them with a list or vector 
>>>>> (in the Racket sense). So I could have:
>>>>>
>>>>> (define (vector i j k) #(i j k))
>>>>> (define (point x y z) #(x y z))
>>>>>
>>>>> Then I could readily use the existing vector functions, such as 
>>>>> vector-map without having to define my own. But I don't super like this 
>>>>> because I have to define my own accessor functions like vector-i and 
>>>>> point-y and also don't get predicates like vector? for free.
>>>>>
>>>>> Another way is that I could use structs, but then I'm stuck 
>>>>> implementing things myself and across the structs. To avoid the latter 
>>>>> point, I could use pattern matching. So something like:
>>>>>
>>>>> (struct vector (i j k))
>>>>> (struct point (x y z))
>>>>>
>>>>> (define (tuple-map proc tuple)
>>>>>   (match tuple
>>>>>     [(struct vector (i j k)) (vector (proc (vector-i tuple))
>>>>>                                      (proc (vector-j tuple))
>>>>>                                      (proc (vector-k tuple)))]
>>>>>     [(struct point (x y z)) (point (proc (point-x tuple))
>>>>>                                    (proc (point-y tuple))
>>>>>                                    (proc (point-z tuple)))]
>>>>>     [(struct color (r g b)) (color (proc (color-r tuple))
>>>>>                                    (proc (color-g tuple))
>>>>>                                    (proc (color-b tuple)))]))
>>>>>
>>>>> But of course, this map doesn't take multiple tuples. And this feels 
>>>>> awkward, because I'll need to implement other things, like fold. Map and 
>>>>> fold would be used in defining new operators on vectors and points, like 
>>>>> addition, normalization (for vectors only), etc.
>>>>>
>>>>> The ideal thing would be that I could define a struct for these types, 
>>>>> that had the accessor functions like vector-i and predicates likes 
>>>>> vector? but was actually represented by a vector (in the Racket 
>>>>> sense) underneath the hood. Does something like this exist in Racket (not 
>>>>> classes please).
>>>>>
>>>>> In F#, I did this same thing using F#'s records for the vector, point, 
>>>>> and color data types, and they inherited an ITuple interface (F#'s 
>>>>> immutable, functional data types can implement interfaces). Can Racket's 
>>>>> stucts inherit from interfaces? Is there something I can do with generics?
>>>>>
>>>>> Thanks for any help on this design.
>>>>>
>>>>

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/e0925bff-c910-4cf2-b58f-810df2d28f58n%40googlegroups.com.

Reply via email to