Hello! I'm trying to use contract with generic functions. However, the code doesn't work as expected:
#lang racket
(require racket/generic)
(define-generics g
(g-func g))
(struct s ()
#:methods gen:g
[(define/contract (g-func x)
(-> s? s?)
x)])
Running the code in DrRacket would result in
s?: undefined;
cannot reference an identifier before its definition
But it's fine to use s? inside the body of g-func, outside the contract definition. e.g. (define (g-func x) (when (s? x) x)). Why does it happen? Am I using contract the wrong way?
I have not used generics enough to know right from wrong, but hopefully if I post a kind-of wrong answer, someone will correct that.
Technically, this works, but I assume there is probably something going on here that should be addressed differently. It is because the binding for s? does not exist yet in the context which the contract is being created. But, by wrapping the procedure, we obviate that (at the cost of some indirection).
Edit:
To be more precise, you can think of the contract as something that is separate from the body of the procedure. So, the body will not be "evaluated" at the same time as the terms for the contract, and so will not be subject to this contextual mismatch. We achieve the same mechanic by wrapping the s? in a lambda.
Thanks for your solution!
It's surprising that wrapping it up in a lambda works. Now I understand that the error
cannot reference an identifier before its definition
occurs when someone tries to access an identifier which is defined but not bound, e.g. (define a a). When writing a contract, the expression is immediately evaluated, so the error occurs. But in the body of g-func, it's okay to use s? because it won't be evaluated until being called.
Although I have almost no hands-on real-world experience with racket/generic, I suspect the intended or typical case is to use define for the #:methods -- not define/contract.
I imagine often people don't use a contact at all (it might be too expensive)?
Or they attach a contract on export with (provide (contract-out ...)) (if that even makes sense for generics)?
Or I suppose you could define/contract a plain old helper function, and the method is a wrapper calling that?
Anyway this is one of my half-arsed answers, the main value of which will be to provoke someone to correct me, and voila, you get that answer.