[racket-dev] Generics scoping issues

From: J. Ian Johnson (ianj at ccs.neu.edu)
Date: Fri Aug 16 17:17:55 EDT 2013

Re: problem 1 - ah yes I see.
Problem 2:
In the body of def-free:

generic-problems.rkt:16:26: define/generic: free is not a method of generic interfaces gen:binds-variables
  at: free
  in: (define/generic gfree free)

This compiles if I additionally supply def-free with free, but it doesn't run:
free-box: undefined;
 cannot reference an identifier before its definition
  in module: "/home/ianj/projects/oaam/code/generic-problems.rkt"
   /home/ianj/projects/oaam/code/generic-problems.rkt: [running body]

Furthermore, if I abstract over the struct form so that #:methods gen:binds-variables is part of the macro expansion, even passing free to def-free won't work anymore:

(define-syntax-rule (astruct name (fields ...) (methods ...))
  (struct name exp (fields ...) #:transparent #:methods gen:binds-variables [methods ...]))

(astruct avar (name)
        [(def-free e gfree free bound avar [(x) (if (x . ∈ . bound) ∅ (set x))])])

generic-problems.rkt:31:27: define/generic: free is not a method of generic interfaces gen:binds-variables
  at: free
  in: (define/generic gfree free)

----- Original Message -----
From: "Carl Eastlund" <cce at ccs.neu.edu>
To: "J. Ian Johnson" <ianj at ccs.neu.edu>
Cc: "dev" <dev at racket-lang.org>
Sent: Friday, August 16, 2013 4:50:53 PM GMT -05:00 US/Canada Eastern
Subject: Re: [racket-dev] Generics scoping issues

Problem 1 -- you have to use define/generic if you want to use the generic version of something in the context of a set of specific method implementations. That's by design. 

Problem 2 -- what error message or unexpected behavior are you getting? That should work, it sounds like a bug in define/generic if it's not working. 

Carl Eastlund 

On Fri, Aug 16, 2013 at 4:36 PM, J. Ian Johnson < ianj at ccs.neu.edu > wrote: 

I'm starting to use generics, and me being myself, I wrote some macros to make writing method definitions easier. 
But, I'm seeing that #:methods seems to rebind method identifiers in a way that hygiene interferes with. 

I would expect to be allowed to do the following two things (problems annotated): 

(struct exp (label fvs-box)) ;; parent struct for all expressions 
(define-generics binds-variables 
[free-box binds-variables] 
[free binds-variables #:bound [bound]] 
#:fallbacks [(define (free e #:bound [bound ∅]) ∅) 
(define free-box exp-fvs-box)] 
#:fast-defaults ([(compose unbox free-box) 
(define (free e #:bound bound) (unbox (free-box e)))])) ;; problem 1: free-box not in scope 

(define-syntax-rule (def-free e gfree bound struct [(pats ...) rhss ...]) 
(define/generic gfree free) ;; problem 2: since #:methods rebinds free, this is not in the scope one would expect with its definition in the define-generics form. 
(define (free e #:bound [bound ∅]) 
(match e [(struct _ fvs-box pats ...) 
(set-box! fvs-box 
(let () rhss ...))])))) 

(struct var exp (name) #:transparent 
#:methods gen:binds-variables 
[(def-free e gfree bound var [(x) (if (x . ∈ . bound) ∅ (set x))])]) 

I have workarounds thanks to stamourv, but they're unpleasant: 
Problem 1: define free in fast-defaults as an eta-expansion of a definition outside the define-generics form that does what you want. 
Problem 2: add free as a parameter to def-free, and pass free in at all uses of def-free. 

The first problem seems like more of a programming error than the use of the wrong tool. 
The second problem seems like generic method identifiers should be syntax-parameters, if they indeed need to be rebound in the rhs of the #:methods argument. 

Are these expectations unreasonable/against the design decisions for generics? 

Racket Developers list: 

Posted on the dev mailing list.