Reputation: 95
I am trying to define a class method like so:
#lang racket
(define foo% (class object%
(super-new)
(define/public plus-one (curry + 1))))
But this code produces the following error:
eval:2:0: class: bad form for method definition
According to the Racket documentation, it should be possible to define a method using the syntax (define/public id expr)
, so I don't understand why this isn't working.
There is an easy workaround by using the (define/public (id . formals) body ...+)
syntax, of course, but I would like to understand why the current code isn't accepted.
Upvotes: 1
Views: 107
Reputation: 10663
In the documentation for class*
, see the nonterminals named method-definition and method-procedure. They describe the constraints on a legal method definition. A paragraph later in the class
docs says that each private
, public
, etc declaration must correspond to a method-definition. So it isn't a bug, it's the intended behavior.
The reason behind the behavior, by the way, is that Racket's class
macro implements methods by rewriting the method's lambda
(or case-lambda
) expression(s) to add an implicit argument for this
. The same argument is also implicitly used to access the object's public and private fields. Restricting method definitions to certain shapes makes it possible to find the right lambda
expressions to adjust.
Here's one way of rewriting your example:
#lang racket
(define do-plus-one (curry + 1))
(define foo% (class object%
(super-new)
(define/public (plus-one n) (do-plus-one n)))
This has the following nice properties: do-plus-one
is computed only once, and it doesn't take a field slot in each foo%
object.
Upvotes: 0
Reputation: 48775
It might be a bug. Imagine we have the usuall way:
(define/public (plus-one n) (+ 1 n))
The define
way with name and arguments in parentheses are sugar for a id
and lambda
and that works:
(define/public plus-one (lambda (n) (+ 1 n)))
I'm getting that (curry + 1)
returns a similar lambda and that that object should be working, but it isn't.
The whole point with a class method is using the private fields from the object and none of them do that, however only curry
version makes it obvious that it will not be able to since the resulting procedure won't be having the scope of the object.
If you imagined we used a private variable for the increment you couldn't use curry
since it would cache it. eg. you can't replicate this:
(define increaser%
(class object%
(init inc)
(define increment inc)
(super-new)
(define/public (set-inc inc)
(set! increment inc))
(define/public (increase n)
(+ increment n))))
(define test (new increaser% [inc 2]))
(send test increase 1) ; ==> 3
(send test set-inc 3)
(send test increase 1) ; ==> 4
(define test2 (new increaser% [inc 21]))
(define value 13)
(map (lambda (obj) (send obj increase value)) (list test test2))
; ==> (16 34)
Upvotes: 0