Does anyone know a way to check a struct instance value and update it?

I am trying to write a function that computes the price needed for a flight upgrade and updates
the points collected. The function takes as argument a holidaymaker struct instance,
and if the upgrade field is true, add an extra 20% to the current flight price and
stores the result in the flights field. E.g., give the following instance “Tom Fields”,
1234, 350, 800, #t, the function will update the flights field with 420.

I thought everything worked fine until I tested the function with another instance where there upgrade field was false, for example, i run the function
(check john-wood)

i then check the updated value of
(holidaymaker-flights john) it returns 408.0,it shouldnt because john has a false value and shouldn't be updated

This is where the problem lies , my function isn't checking if a is an instances - with a specific value. For instance is john, has a value of true, because i did try putting a and #t in the same bracket but it wont let me have two argument's in the same bracket, anyone know a way to check if a value is bound to an instance and act upon it.
(define john-wood (holidaymaker "john wood" 1255 340 600 #f))

(define (check a)
(and (holidaymaker? a) (holidaymaker-upgrade a) #t)
(set-holidaymaker-flights! a (* (holidaymaker-flights a) 1.2)))

2 Likes

I think the error is in the and statement. and returns #t or #f depending on the whether all statements inside of it resolve to #t or #f. Right now the way you have it written if you put john-wood in the function you get.

(and (holidaymaker? john-wood) (holidaymaker-upgrade john-wood) #t)
(set-holidaymaker-flights! john-wood (* (holidaymaker-flights a) 1.2)))

in the and expression (holidaymaker? john-wood) resolves to #t (holidaymaker-upgrade john-wood) resolves to #f and the bare #t resolves to #t) so you get (and #t #f #t) which returns #f so now you have

#f
(set-holidaymaker-flights! john-wood (* (holidaymaker-flights a) 1.2)))

The value of and does nothing to control what happens next, so (set-holidaymaker-flights! john-wood (* (holidaymaker-flights a) 1.2)) will always execute no matter what the and statement returns. What you need to do is add some control flow to the function. Either a when statement or an if statement. And there needs to be a comparison between the value returned by (holidaymaker-flights a) and the value that is returned.

Actually, and returns #f if at least one of the statements it contains is false, otherwise it returns the result of the last expression.

> (and 7 9)
9
> (and #f 9)
#f
> (and (holidaymaker? john-wood) (holidaymaker-upgrade john-wood) #t)
#t
> (and (holidaymaker? john-wood) (holidaymaker-upgrade john-wood) )
<the result of (holidaymaker-upgrade john-wood)>

@codinggeek46 The reason that john-wood isn't being updated is because his upgrade field is #f. It works fine if you make it #t.

#lang racket

(struct holidaymaker (name points price x upgrade?) #:transparent #:mutable) ; dunno what x is                
(define john-wood (holidaymaker "john wood" 1255 340 600 #f))
(define jane-wood (holidaymaker "jane wood" 1255 340 600 #t))
(define (check person)
  (if (holidaymaker-upgrade? person)
      (and (set-holidaymaker-price! person (* 1.2 (holidaymaker-price person)))
           person) ; update and return                                                                        
      person))

(display "John is: ") john-wood
(display "Jane is: ") jane-wood
(check john-wood)
(check jane-wood)

As a thought, although mutating the structure works, it goes against the idea of functional programming that is at the core of Racket. The point of FP is that it offers you guarantees of safety -- in this case, you know that your data is not going to change under you because some other function in the call stack modified it, or because some other thread diddled with it. Under a functional model, instead of modifying an existing value, you create a new one. Here's one option:

#lang racket

(struct holidaymaker1 (name points price x upgrade?) #:transparent) ; dunno what x is
(define jane-wood (holidaymaker1 "jane wood" 1255 340 600 #t))

(define (upgrade-manually person)
  (cond [(holidaymaker1-upgrade? person)
         ; parse the struct and grab the various fields into new variables with the same name as the field
         (match-define (struct holidaymaker1 ( name points price x upgrade?))
           person)
         (holidaymaker1 name points (* price 1.2) x upgrade?)]; manually construct a new instance
        [else person]))

(display "Jane is: ") jane-wood ; REPL shows:  Jane is: (holidaymaker1 "jane wood" 1255 340 600 #t)

(upgrade-manually jane-wood) ; REPL shows: (holidaymaker1 "jane wood" 1255 408.0 600 #t)

; Alternatively, you could have a library do the work:
(require struct-plus-plus) ; blatant self-plug here.  I wrote it
(struct++ holidaymaker2 (name points price x upgrade?) #:transparent) ; dunno what x is
(define alice-white (holidaymaker2 "alice white" 1255 340 600 #t))

(define (upgrade-via-spp person)
  (cond [(holidaymaker2-upgrade? person)
         ; parse the struct and grab the various fields into new variables with the same name as the field
         (match-define (struct holidaymaker2 (name points price x upgrade?))
           person)
         (set-holidaymaker2-price person (* price 1.2))] ; functional setter was auto-generated
        [else person]))

(display "Alice is: ") alice-white ; REPL shows: Alice is: (holidaymaker2 "alice white" 1255 340 600 #t)

(upgrade-via-spp alice-white)  ; REPL shows: (holidaymaker2 "alice white" 1255 408.0 600 #t)

;  Using struct-plus-plus...
(struct++ holidaymaker3 (name points price x upgrade?) #:transparent)

; ...gives you a keyword constructor
(define nate-ford
  (holidaymaker3++ #:name     "Nate Ford"
                   #:points   1700
                   #:price    180.33
                   #:x        7
                   #:upgrade? #t))
nate-ford   ; REPL shows: (holidaymaker3 "Nate Ford" 1700 180.33 7 #t)

; It also lets you enforce data integrity and have optional parameters
(struct++ holidaymaker4 ([name          string?]
                         [points        exact-positive-integer?]
                         [price         (and/c positive? real?)]
                         x
                         [(upgrade? #f) boolean?]) #:transparent)

(define  alex-hardison
  (holidaymaker4++ #:name     "Alex Hardison"
                   #:points   1900
                   #:price    172.57
                   #:x        7
                   )) ; the upgrade? field defaults to #f

(define parker
  (holidaymaker4++ #:name   2100      ; oops, someone misremembered the order of the first two fields
                   #:points "Parker"  ;
                   #:price  180.33
                   #:x      7))
; REPL shows:
; holidaymaker4++: contract violation                                                                         
;   expected: string?                                                                                         
;   given: 2100                                                                                               
;   in: the #:name argument of                                                                                
;       (->*                                                                                                  
;        (#:name                                                                                              
;         string?                                                                                             
;         #:points                                                                                            
;         exact-positive-integer?                                                                             
;         #:price                                                                                             
;         (and/c positive? real?)                                                                             
;         #:x                                                                                                 
;         any/c)                                                                                              
;        (#:upgrade? boolean?)                                                                                
;        holidaymaker4?)                                                                                      
;   contract from: (function holidaymaker4++)                                                                 
;   blaming: /Users/dstorrs/bmtc_dev/app/resources/test.rkt                                                   
;    (assuming the contract is correct)                                                                       
;   at: /Users/dstorrs/bmtc_dev/app/resources/test.rkt                                                        
; Context (plain; to see better errortrace context, re-run with C-u prefix):                                  
;   /Applications/Racket_v8.4/collects/racket/contract/private/blame.rkt:346:0 raise-blame-error              
;   /Applications/Racket_v8.4/collects/racket/contract/private/arrow-higher-order.rkt:387:44                  
;   /Applications/Racket_v8.4/collects/racket/private/kw.rkt:1951:33 kw-chaperone                             
>