1
votes

I'm following realm of racket. The code below throws the following error:

coord-lat: contract violation
expected: coord?
given: #<void>

fly-ufo.rkt

#lang racket

(require 2htdp/universe 2htdp/image)
(struct coord (alt lat) #:transparent #:mutable)

(define WIDTH 400)
(define HEIGHT 400)
(define UFO *some_picture*)

(define (move-ufo pos)
  (place-image/align UFO
                     (coord-alt pos)
                     (coord-lat pos)
                     "left" "top"
                     (empty-scene WIDTH HEIGHT)))

(define (increment pos)
  (set-coord-alt! pos (+ 2 (coord-alt pos)))
  (set-coord-lat! pos (+ 1 (coord-lat pos))))

 (define (at-the-border pos)
  (>= (coord-lat pos) (- HEIGHT 20)))

(big-bang (coord 0 0)
          (on-tick increment)
          (to-draw move-ufo)
          (stop-when at-the-border))

Adding a (coord? pos) conditional for debug affirmates that the coordinate is not a coord. Defining the current-state as a coord before the big-bang function as below also didn't solve the error:

(define s (coord 0 0))

(big-bang s...

I checked racket contract and struct documents, but couldn't find the cause. Since many examples used define-struct, I also tried it and make-coord, but this also didn't help. Racket document also suggest that struct is preferred.

Adding a contract to the coord

(provide coord)
(define-struct/contract coord ([alt number?]
                               [lat number?])
  #:transparent
  #:mutable)

causes the following error:

    coord-lat: contract violation
 expected: coord?
 given: #<void>
 in: the 1st argument of
      (-> coord? number?)
 contract from: (struct coord)
 blaming: fly-ufo.rkt
 at: fly-ufo.rkt

What may be the cause of the error here?

EDIT

@Greg Hendershott: set-coord-alt! is in the struct document of racket as:

set-id-field-id!, for each field that includes a #:mutable option, or when the #:mutable option is specified as a struct-option; a mutator procedure that takes an instance of the structure type and a new field value. The structure is destructively updated with the new value, and #<void> is returned.

So, when I read the last sentence, I got why the function returns #<void>, which I somehow missed before. So, in order to return a coord instead of a #<void>, I added a pos in the end of the increment function:

(define (increment pos)
  (set-coord-alt! pos (+ 2 (coord-alt pos)))
  (set-coord-lat! pos (+ 1 (coord-lat pos)))
  pos)

and it worked.

2nd EDIT

I'm putting this just for future reference. After Metaxal noted that one should create a new struct, I updated the struct as follows:

;; a struct for the current state, where:
;; field 1 is x coordinate
;; field 2 is y coordinate
;; field 3 is horizontal speed
;; field 4 is vertical speed
(struct cs (lat alt hspeed vspeed))

and thereafter, stopping the ufo became as simple as:

(define (stop ufo)
  (cs (cs-lat ufo) (cs-alt ufo) 0 0))

and speeding upwards became:

(define (up-wards ufo)
  (cond [(> (cs-vspeed ufo) (- MAX-SPEED))
         (struct-copy cs ufo [vspeed (sub1 (cs-vspeed ufo))])]
        [#t (play-sound some-sound-file #t)
            ufo]))

Now the only thing I wonder is, whether it is imperative style to put play-sound-file and ufo in a cascade in the else branch.

1
This sure works, but it's not the preferred way to do things in Racket and functional languages. Instead of "mutating" (modifying) an existing pos, you should remove the #:mutable keyword in the struct definition, and in your increment function you should create a new pos (which is actually simpler than mutating one, here).Metaxal
Oh I see, that's what "destructively" means. I like the functional way and am trying to learn the systematic, but I have a hard time figuring out the mutation. Thank you for the hint.barerd
@Metaxal: I tried to create a new pos using struct-copy and some begin statements, but couldn't succeed. Could you very mind to guide me to a document about it?barerd
For this simple struct, you don't really need struct-copy. Simply (define (increment pos) (coord (coord-alt pos) (coord-lat pos))) should do the job. But if you do want to experiment with struct-copy (which is indeed better if the struct is complicated), I think the standard docs should be helpful, if you haven't found them yet: docs.racket-lang.org/reference/struct-copy.htmlMetaxal
I got it, just do a (coord ...) inside a function define.barerd

1 Answers

0
votes

Generally you'll get an error like given: #<void> when a function is supposed to return a value, but doesn't. For example, bad:

(define (increment x)
  (set! x (add1 x)))
;; set! returns void, therefore increment returns void

vs. good:

(define (increment x)
  (add1 x))
;; returns a number

Although I'm not familiar with big-bang it looks like you have a similar issue:

(define (increment pos)
  (set-coord-alt! pos (+ 2 (coord-alt pos)))
  (set-coord-lat! pos (+ 1 (coord-lat pos))))

  . . . .

(big-bang (coord 0 0)
          (on-tick increment)
          (to-draw move-ufo)
          (stop-when at-the-border))

It looks like all the variants of the on-tick clause in big-bang are supposed to return HandlerResult, not #<void>.

Source: http://docs.racket-lang.org/teachpack/2htdpuniverse.html#(form._world._((lib._2htdp/universe..rkt)._to-draw))

What is set-coord-alt! and what does it return?