Button click callback syntax

35 views
Skip to first unread message

Philip Benade

unread,
May 12, 2020, 12:34:50 PM5/12/20
to Racket Users
Hi All

I am new to Racket and I have found a situation where I'm not certain why Racket works the way it does. Unfortunately I am not sure of the vocabulary so I will describe the situation with some code:


; A button made this way works as I would expect, it waits for 30 minutes and then shows a message-box and plays a sound.
(new button% [parent long-break-panel]
             [label "Long Break"]
             ; Callback procedure for a button click:
             [callback (lambda (button event)
                         (send long-break-msg set-label "Long break timer started")
                         (general-timer 30 long-break-callback))])

; A button made this way immediately shows the message box and plays the sound.
(new button% [parent long-break-panel]
             [label "Long Break"]
             ; Callback procedure for a button click:
             [callback (lambda (button event)
                         (send long-break-msg set-label "Long break timer started")
                         (general-timer 30 (general-callback long-break-msg "Not on long break" "30 Minutes have passed, back to work.")))])

; A button made this way also immediately shows the message box and plays the sound. Why is it different when the function name is surrounded by parentheses?
(new button% [parent long-break-panel]
             [label "Long Break"]
             ; Callback procedure for a button click:
             [callback (lambda (button event)
                         (send long-break-msg set-label "Long break timer started")
                         (general-timer 30 (long-break-callback)))])

; The documentation for buttons say they require a callback. I interpreted this to mean a function that will execute when the button is clicked.
(define general-callback
  (lambda (msg-name msg-text message-box-text)
    (time-expired-sound)
    (send msg-name set-label msg-text)
    (message-box "Timer expired" message-box-text frame)))

; Why does wrapping the function so that it takes no parameters change how it gets executed?
(define long-break-callback
  (lambda ()
    (general-callback long-break-msg "Not on long break" "30 Minutes have passed, back to work.")))


Is anyone able to explain why I am getting these different behaviors from these buttons?

Regards
Philip

David Storrs

unread,
May 12, 2020, 2:08:58 PM5/12/20
to Philip Benade, Racket Users
Hi Philip,

The essence here is when evaluation happens:

; All of these are equivalent:
(define foo (lambda () 7)
(define foo (thunk 7)) ; 'thunk' is a term of art meaning "function
of 0 arguments"
(define (foo) 7)

Try running the following code in the repl:
; start code
(define (general-callback msg-name msg-text message-box-text) ;
function of 3 arguments
'result-of-general-callback)
general-callback
(general-callback "name" "text" "msg box text")
; end code

You'll see that 'general-callback' returns:
#<procedure:general-callback>, which is the actual function
and (general-callback ...) returns 'result-of-general-callback, which
is the result of invoking the function.


In your GUI code, you want to be passing the function itself, not the
result of invoking the function.

Does that make sense?
> --
> 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 racket-users...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/3a6effce-915e-42c8-aff7-56fc23524e4c%40googlegroups.com.

gfb

unread,
May 12, 2020, 2:37:32 PM5/12/20
to Racket Users
The difference is whether you are evaluating an expression, versus
putting it inside a function to be evaluated when the function is called.

(require racket/gui)

; Call message-box, get an immediate effect ...
(message-box "What do you think?" "hi, would you like a tomato" #f '(yes-no))
; The result ('yes or 'no) is printed in the repl/Interactions.

; Create a function/procedure, that takes no arguments, and its body calls message-box ...
(lambda () (message-box "Greeting" "hi, would you like a tomatillo" #f '(yes-no)))
; That which prints as #<procedure> since it's a function, and we didn't call it.

; To call a function we put parentheses around it ...
((lambda () (message-box "Greeting" "hi, would you like a pomodoro" #f '(yes-no)))
 )
; That evaluated the body.

; Name a function ...
(define greeter (lambda () (message-box "Greeting" "hi, would you like a green tomato" #f '(yes-no))))
; That didn't call it, and neither do ...
greeter
greeter
; Those just mention the function, which prints as #<procedure>.
; Call it to evaluate the body ..
(greeter)


By the way, your examples that don't wait 30 minutes actually do,
and if you wait 30 minutes they will try to call the callback, which
is not a function but the 'ok result from the message-box.

Try setting the timer to something shorter, and then wait after
you click the immediate message box, then when the timer
goes off there will be an error something like ...
application: not a procedure;
 expected a procedure that can be applied to arguments
  given: 'ok
  arguments...: [none]


Philip Benade

unread,
May 13, 2020, 6:21:28 AM5/13/20
to Racket Users
Hi David

Thank you for replying to my question. 

I have a much better understanding of what is happening now, especially about passing a function instead the result of a function. Thank you.

Regards
Philip
> To unsubscribe from this group and stop receiving emails from it, send an email to racket...@googlegroups.com.

Philip Benade

unread,
May 13, 2020, 6:35:19 AM5/13/20
to Racket Users
Hi gfb

Thank you for replying to my question.

Your reply has saved me from writing unnecessary boiler plate code for my click events. I see the difference between mentioning a function and evaluating it now.

What I ended up going for looks like this:

; Make a button in the frame
(new button% [parent work-panel]
             [label "Work"]
             ; Callback procedure for a button click:
             [callback (lambda (button event)
                         (send work-msg set-label "Work timer started")
                         (make-timer 25 (lambda ()
                                          (timer-callback work-msg "Not working" "25 Minutes have passed, take a break."))))])

Regards
Philip
Reply all
Reply to author
Forward
0 new messages