'How to access sub-struct variables in Racket (Lisp/Scheme)

I'm trying to write an AM/PM clock conversion function to a 24 hour clock (military time), but there's a struct and sub-struct that have variables I can't figure out how to pull. Neither 24HourClock-hours or AM/PM-hours is working to pull the hours or minutes variable out of the AM/PM instance of time that goes as the parameter of each of the two versions of the function.

The following two functions are two separate attempts at the same function:

(define (AM/PM->24HourClock2 time)
  (cond
   [(isAM? time) (24HourClock (24HourClock-hours 24HourClock-minutes))]
   [else (24HourClock (+ 24HourClock-hours 12) 24HourClock-minutes)]))
(define (AM/PM->24HourClock time)
  (cond
   [(isAM? time) (24HourClock (AM/PM-hours AM/PM-minutes))]
   [else (24HourClock (+ AM/PM-hours 12) AM/PM-minutes)]))

This one attempts to use a lambda function to access the sub-members:

(define (AM/PM->24HourClock time)
  (lambda (hr min meridiem)
    (cond
      [(isAM? time) (24HourClock (hr min))]
      [else (24HourClock (+ hr 12) min)]))

But here's the output:

output

Here's the earlier code that makes it reproducible:

#lang slideshow

(struct 24HourClock (hours minutes)
  #:transparent
  #:mutable
  #:guard (lambda (hours minutes err_name)
    (if (not (and (>= hours 0) (< hours 24)))
     (error err_name "Hours must be between 0 and 24")
    (if (not (and (>= minutes 0) (< minutes 60)))
     (error err_name "Minutes must be between 0 and 60")
    (values hours minutes)))))

(struct AM/PM 24HourClock (meridiem)
  #:transparent
  #:mutable
  #:guard (lambda (hours minutes meridiem err_name)
            (if (not(and(and(number? hours)(> hours 0)(<= hours 12)(number? minutes))))
                (error err_name "Hours must be between 1 and 12 : 0")
                    (if [not (and(string? meridiem)(or
                         (equal? meridiem "am")
                         (equal? meridiem "AM")
                         (equal? meridiem "pm")
                         (equal? meridiem "PM")))]                    
                    (error err_name "Invalid value for meridiem")
                    (values hours minutes meridiem)))))
       
 (define (isAM? time)
    (or (equal? (AM/PM-meridiem time) "AM")
        (equal? (AM/PM-meridiem time) "am")))


Solution 1:[1]

It should be rather

(define (AM/PM->24HourClock2 time)
  (cond
   [(isAM? time) (24HourClock (AM/PM-hours time) (AM/PM-minutes time))]
   [else (24HourClock (+ (AM/PM-hours time) 12) (AM/PM-minutes time))]))

(define (AM/PM->24HourClock time)
  (let ((hr (AM/PM-hours time))
        (min (AM/PM-minutes time))
        (mer (AM/PM-meridiem time)))
    (cond
      [(isAM? time) (24HourClock hr min)]
      [else (24HourClock (+ hr 12) min)])))

But since AM/PM-hours and AM/PM-minutes are not defined when defining like you did here with 24HourClock as super class, you have to write:

(define (AM/PM->24HourClock2 time)
  (cond
   [(isAM? time) (24HourClock (24HourClock-hours time) (24HourClock-minutes time))]
   [else (24HourClock (+ (24HourClock-hours time) 12) (24HourClock-minutes time))]))

(define (AM/PM->24HourClock time)
  (let ((hr (24HourClock-hours time))
        (min (24HourClock-minutes time))
        (mer (AM/PM-meridiem time)))
    (cond
      [(isAM? time) (24HourClock hr min)]
      [else (24HourClock (+ hr 12) min)])))

Your problem is that you think struct-slot is itself the value of slot in struct. But it isn't. It is just the getter function for the slot in struct. Therefore, you have to do (struct-slot instance) to get the value of slot in the instance which is of the type struct.

If you want AM/PM->24HourClock, then time is of the type AM/PM.

Solution 2:[2]

This is all code put together, it's the same as your response and what was shown:

#lang slideshow

(struct 24HourClock (hours minutes) #:transparent #:mutable
  #:guard (lambda (hours minutes err_name)
    (if (not (and (>= hours 0) (< hours 24)))
     (error err_name "Hours must be between 0 and 24")
    (if (not (and (>= minutes 0) (< minutes 60)))
     (error err_name "Minutes must be between 0 and 60")
    (values hours minutes)))))

(struct AM/PM 24HourClock (meridiem) #:transparent #:mutable
  #:guard (lambda (hours minutes meridiem err_name)
            (if (not(and(and(number? hours)(> hours 0)(<= hours 12)(number? minutes))))
                (error err_name "Hours must be between 1 and 12 : 0")
                    (if [not (and(string? meridiem)(or
                         (equal? meridiem "am")
                         (equal? meridiem "AM")
                         (equal? meridiem "pm")
                         (equal? meridiem "PM")))]                    
                    (error err_name "Invalid value for meridiem")
                    (values hours minutes meridiem)))))
         
 (define (isAM? time)
    (or (equal? (AM/PM-meridiem time) "AM")
        (equal? (AM/PM-meridiem time) "am")))

(define (AM/PM->24HourClock time)
  (let ((hr (AM/PM-hours time))
        (min (AM/PM-minutes time))
    (cond
      [(isAM? time) (24HourClock hr min)]
      [else (24HourClock (+ hr 12) min)])))

error

Here's what I use for test cases:

;=======================
;    Test Cases
;=======================

; (24HourClock 0 20)
; (24HourClock 24 20)
; (24HourClock 11 60)
; (AM/PM 0 20 "AM")
; (AM/PM 1 20 "Am")
; (AM/PM 2 20 "am")
; (is-AM? 2 20 "am")

; (AM/PM->24HourClock (AM/PM 0 10 "AM"))
; (AM/PM->24HourClock (AM/PM 12 10 "am"))

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 cocoakrispies93