Parameterize not working as expected in 8.13

I expected (host) to change to "hello" and then to "hi".
I'm not understanding how it's becoming #t at all!

#lang racket/base

(define *host* (make-parameter "ngender.net" string? 'host))

(eprintf "[0] host ~a\n" (*host*))

(parameterize ( [*host* "hello"] )
  (eprintf "[1] host ~a\n" (*host*))
  (*host* "hi")
  (eprintf "[2] host ~a\n" (*host*)) )

;; prints:
;; [0] host ngender.net
;; [1] host #t
;; [2] host #t

;; racket version: 8.13 [cs]

For a parameter created with a guard function, the result of the guard function is used instead of the value passed in. (The guard function is not applied to the initial value.) So when you write (parameterize ([*host* "hello"]) ...) or (*host* "hi"), the parameter is set to #t because that is the result of (string? "hello") and (string? "hi").

You could either change the guard function to something like:

(λ (v)
  (if (string? v)
      v
      (raise-argument-error '*host* "string?" v)))

or use a contract, e.g.:

(define/contract *host*
  (parameter/c string?)
  (make-parameter "ngender.net" #f 'host))
1 Like

I made this mistake before. The name "guard" is kinda misleading.

I think the documentation should be updated to include some examples on how how to use the guard argument correctly.

2 Likes

Thank you! Further confusing the issue, the "guard" isn't used to check the original value! (Just the opposite as with structs!).
I'm now using my own constructor for parameters:

(define (my-parameter value guard name [check-name #f])
  (define (string x) (format "~a" x))
  (define (check v)
    (unless (guard v)
      (raise-argument-error (string name)
                            (string (or check-name (object-name guard) 'check))
                            v ) )
    v )
  (make-parameter (check value) check name) )
1 Like

I made this mistake before. The name "guard" is kinda misleading.

Ditto.

Maybe we could use the term transforming-guard in the documentation?

It works just as with structs, actually: the guard for a struct should return all values (as multiple values) for the fields, and a parameter is conceptually also a struct with one field. If you don’t need to do anything with the values, just return them as is. The reason guards work this way is so that guards can do more than checking, such as normalizing the input values (as with “Boolean parameters”).

2 Likes

Pro tip: (require racket/format) provides ~a as a procedure for turning things into strings.

1 Like