[racket] How to get arity of make-object? Found answer

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Fri Jun 6 01:31:27 EDT 2014

Sorry for being so late to the discussion, but I agree that something
like `class-constructor-arity` and `class-constructor-arity-includes?`
should be added to `racket/class`.

At Fri, 06 Jun 2014 05:24:36 +0400, Roman Klochkov wrote:
>  Hm... But reading it's sources give me some clues.
> 
> Now I have working dirty hack
> 
> #lang racket/base
> (require racket/private/class-internal)
> 
> (define (class-inits cls)
>   (apply append 
>     (for/list ([c (in-vector (class-supers cls))]) 
>       (class-init-args c))))
> 
> It gives the list of class init arguments. Maybe someone also needs it.
> Thu, 5 Jun 2014 21:13:47 -0400 от Matthias Felleisen <matthias at ccs.neu.edu>:
> >
> >class/c doesn't check inits properly: 
> >
> >>#lang racket
> >>
> >>(provide
> >>  (contract-out
> >>    [c% (class/c (init-field [x number?]))]))
> >>
> >>(define c%
> >>  (class object%
> >>    (field (x 10))
> >>    (super-new)))
> >
> >and now run
> >
> >>Welcome to Racket v6.0.1.12.
> >>> (require "foo.rkt")
> >>> (new c%)
> >>(object:c% ...)
> >Okay more thinking to do -- Matthias
> >
> >
> >
> >
> >On Jun 5, 2014, at 8:49 PM, Roman Klochkov wrote:
> >>It is maybe closer.
> >>
> >>But inside define-binary-class I also doesn't know the total number of init 
> arguments. Because library user may give any superclass. And I don't know, how 
> to get init arguments (or arity for of make-object for a given class) without 
> calling make-object and reading the error message.
> >>Lake in case
> >>(define base% (class (super-new) (init-field param1 param2)))
> >>(define-binary-class db base% ((a u1) (b u2)))
> >>
> >>Maybe the problem doesn't have a solution. But I see class/c, which has init 
> and init-fields clauses. Somehow it checks them...
> >>
> >>Thu, 5 Jun 2014 16:36:16 -0400 от Matthias Felleisen < matthias at ccs.neu.edu >:
> >>>
> >>>Sorry I am so slow. 
> >>>
> >>>Contracts are first-class values. 
> >>>
> >>>You could modify define-binary-class so that it also creates and defines
> >>>a contract P that can be passed along to read-object. Then read-object
> >>>can use P like this: 
> >>>
> >>>> #lang racket
> >>>> 
> >>>> (provide
> >>>>   a% a?
> >>>>   b% b?
> >>>>   (contract-out
> >>>>     (read-object (->i ((c? contract?) (c% any/c))
> >>>> 		   #:rest [l (c?) (lambda (x) (apply c? x))]
> >>>> 		   (_ (c%) (is-a?/c c%))))))
> >>>> 
> >>>> (define a% (class object% [init-field x] (super-new)))
> >>>> (define b% (class object% [init-field x y] (super-new)))
> >>>> 
> >>>> (define a? (lambda x (and (= (length x) 1) (andmap number? x))))
> >>>> (define b? (lambda x (and (= (length x) 2) (andmap number? x))))
> >>>> 
> >>>> (define (read-object __c c% . x)
> >>>>   (apply make-object c% x))
> >>>
> >>>Then you get exactly what you want and pretty precise checking: 
> >>>
> >>>> % !!
> >>>> racket
> >>>> Welcome to Racket v6.0.1.11.
> >>>> > (require "foo.rkt")
> >>>> foo.rkt:9:23: is-a/c?: unbound identifier in module
> >>>>   in: is-a/c?
> >>>>   context...:
> >>>>    standard-module-name-resolver
> >>>>    /Users/matthias/plt/racket/collects/racket/private/misc.rkt:87:7
> >>>> > (require "foo.rkt")
> >>>> foo.rkt:9:31: c%: unbound identifier in module
> >>>>   in: c%
> >>>>   context...:
> >>>>    standard-module-name-resolver
> >>>>    /Users/matthias/plt/racket/collects/racket/private/misc.rkt:87:7
> >>>> > (require "foo.rkt")
> >>>> > ( read-object b? b% 10 20 )
> >>>> (object:b% ...)
> >>>> > ( read-object b? b% 10 )
> >>>> read-object: contract violation
> >>>>   expected: ...vn/2HtDP/foo.rkt:8:34
> >>>>   given: '(10)
> >>>>   in: the l argument of
> >>>>       (->i
> >>>>        ((c? contract?) (c% any/c))
> >>>>        #:rest
> >>>>        (l (c?) (lambda (x) (apply c? x)))
> >>>>        (_ (c%) (is-a?/c c%)))
> >>>>   contract from: 
> >>>>       /Users/matthias/svn/2HtDP/foo.rkt
> >>>>   blaming: top-level
> >>>>   at: /Users/matthias/svn/2HtDP/foo.rkt:7.5
> >>>>   context...:
> >>>>    
> /Users/matthias/plt/racket/collects/racket/contract/private/blame.rkt:143:0: 
> raise-blame-error16
> >>>>    arg-checker
> >>>>    /Users/matthias/plt/racket/collects/racket/private/misc.rkt:87:7
> >>>> > ( read-object a? a% 10 )
> >>>> (object:a% ...)
> >>>> > ( read-object a? a% 10 20)
> >>>> read-object: contract violation
> >>>>   expected: ...vn/2HtDP/foo.rkt:8:34
> >>>>   given: '(10 20)
> >>>>   in: the l argument of
> >>>>       (->i
> >>>>        ((c? contract?) (c% any/c))
> >>>>        #:rest
> >>>>        (l (c?) (lambda (x) (apply c? x)))
> >>>>        (_ (c%) (is-a?/c c%)))
> >>>>   contract from: 
> >>>>       /Users/matthias/svn/2HtDP/foo.rkt
> >>>>   blaming: top-level
> >>>>   at: /Users/matthias/svn/2HtDP/foo.rkt:7.5
> >>>>   context...:
> >>>>    
> /Users/matthias/plt/racket/collects/racket/contract/private/blame.rkt:143:0: 
> raise-blame-error16
> >>>>    arg-checker
> >>>>    /Users/matthias/plt/racket/collects/racket/private/misc.rkt:87:7
> >>>
> >>>
> >>>
> >>>If you want to make sure that a? and a% come together, you can package them 
> up in structs. 
> >>>
> >>>If you just want to make sure that these contracts come from your binary 
> class module, you can hide them in applicable structs or something like that. 
> >>>
> >>>Am I getting closer? -- Matthias
> >>>
> >>>
> >>>
> >>>
> >>>On Jun 5, 2014, at 4:15 PM, Roman Klochkov < kalimehtar at mail.ru > wrote:
> >>>
> >>>> I don't create classes. I provide a function read-object, so user can do
> >>>> 
> >>>> (require binary-class)
> >>>> (define base% (class (super-new) (init-field param1 param2)))
> >>>> (define-binary-class db base% ((a u1) (b u2)))
> >>>> 
> >>>> (define data (read-object db my-file data-param1 data-param2)
> >>>> 
> >>>> And I try to make a contract, so when he made an error in last call and 
> wrote
> >>>> 
> >>>> (define data (read-object db my-file data-param1)), then error message 
> whould blame read-object, not `instantiate'
> >>>> 
> >>>> Thu, 5 Jun 2014 16:08:39 -0400 от Matthias Felleisen < 
> matthias at ccs.neu.edu >:
> >>>> 
> >>>> Can't you use is-a?/c like this: 
> >>>> 
> >>>> > #lang racket
> >>>> > 
> >>>> > (provide
> >>>> > a%
> >>>> > b%
> >>>> > (contract-out
> >>>> > (read-object (case->
> >>>> > [-> (is-a?/c a%) number? number?]
> >>>> > [-> (is-a?/c b%) number? number? number?]))))
> >>>> > 
> >>>> > (define a% (class object% (super-new)))
> >>>> > (define b% (class object% (super-new)))
> >>>> > 
> >>>> > (define read-object
> >>>> > (case-lambda
> >>>> > [(a x) x]
> >>>> > [(b x y) (+ x y)]))
> >>>> 
> >>>> 
> >>>> and use this module like this: 
> >>>> 
> >>>> > % racket
> >>>> > Welcome to Racket v6.0.1.11.
> >>>> > > (require "foo.rkt")
> >>>> > > (read-object (new a%) 10)
> >>>> > 10
> >>>> > > (read-object (new a%) 10 20)
> >>>> > read-object: contract violation
> >>>> > expected: (is-a?/c b%)
> >>>> > given: (object:a% ...)
> >>>> > in: the domain of
> >>>> > the 2nd case of
> >>>> > (case->
> >>>> > (-> (is-a?/c a%) number? number?)
> >>>> > (-> (is-a?/c b%) number? number? number?))
> >>>> > contract from: 
> >>>> > /Users/matthias/svn/2HtDP/foo.rkt
> >>>> > blaming: top-level
> >>>> > at: /Users/matthias/svn/2HtDP/foo.rkt:7.5
> >>>> > context...:
> >>>> > 
> /Users/matthias/plt/racket/collects/racket/contract/private/blame.rkt:143:0: 
> raise-blame-error16
> >>>> > /Users/matthias/plt/racket/collects/racket/private/misc.rkt:87:7
> >>>> > > (read-object (new b%) 10 20)
> >>>> > 30
> >>>> > > (read-object (new b%) 10)
> >>>> > read-object: contract violation
> >>>> > expected: (is-a?/c a%)
> >>>> > given: (object:b% ...)
> >>>> > in: the domain of
> >>>> > the 1st case of
> >>>> > (case->
> >>>> > (-> (is-a?/c a%) number? number?)
> >>>> > (-> (is-a?/c b%) number? number? number?))
> >>>> > contract from: 
> >>>> > /Users/matthias/svn/2HtDP/foo.rkt
> >>>> > blaming: top-level
> >>>> > at: /Users/matthias/svn/2HtDP/foo.rkt:7.5
> >>>> > context...:
> >>>> > 
> /Users/matthias/plt/racket/collects/racket/contract/private/blame.rkt:143:0: 
> raise-blame-error16
> >>>> > /Users/matthias/plt/racket/collects/racket/private/misc.rkt:87:7
> >>>> 
> >>>> 
> >>>> 
> >>>> On Jun 5, 2014, at 3:19 PM, Roman Klochkov < kalimehtar at mail.ru > wrote:
> >>>> 
> >>>> > (define (read-object binary-class in . args)
> >>>> > (send (apply make-object binary-class args) read in))
> >>>> > 
> >>>> > read-object takes a class as a first arg, and additional arguments to 
> make an object of that class.
> >>>> > 
> >>>> > I can't make case-> because I don't know j,ject of what (user-defined) 
> class will be created with the function by user of my library.
> >>>> > 
> >>>> > This case is described in Racket Guide 7,3.9  
> http://docs.racket-lang.org/guide/contracts-general-functions.html#%28part._cont
> racts-no-domain%29 , where recommended to use unconstrained-domain-> with 
> procedure-arity-includes? , but it doesnt' work with make-object
> >>>> > 
> >>>> > Thu, 5 Jun 2014 14:51:21 -0400 от Matthias Felleisen < 
> matthias at ccs.neu.edu >:
> >>>> > 
> >>>> > From what I understand now, you want a contract for a function that 
> creates objects from a variable number of arguments. If you write your module 
> interface like this, 
> >>>> > 
> >>>> > > #lang racket
> >>>> > > 
> >>>> > > (provide
> >>>> > > (contract-out
> >>>> > > (read-object (case->
> >>>> > > [-> 'a number? number?]
> >>>> > > [-> 'b number? number? number?]))))
> >>>> > > 
> >>>> > > (define read-object
> >>>> > > (case-lambda
> >>>> > > [(a x) x]
> >>>> > > [(b x y) (+ x y)]))
> >>>> > 
> >>>> > you can get checked variable-arity behavior: 
> >>>> > 
> >>>> > > Welcome to Racket v6.0.1.11.
> >>>> > > > (require "foo.rkt")
> >>>> > > > (read-object 'a 10)
> >>>> > > 10
> >>>> > > > (read-object 'b 10 20)
> >>>> > > 30
> >>>> > > > (read-object 'b 10)
> >>>> > > read-object: contract violation
> >>>> > > expected: (quote a)
> >>>> > > given: 'b
> >>>> > > in: the domain of
> >>>> > > the 1st case of
> >>>> > > (case->
> >>>> > > (-> 'a number? number?)
> >>>> > > (-> 'b number? number? number?))
> >>>> > > contract from: 
> >>>> > > /Users/matthias/svn/2HtDP/foo.rkt
> >>>> > > blaming: top-level
> >>>> > > at: /Users/matthias/svn/2HtDP/foo.rkt:5.5
> >>>> > > context...:
> >>>> > > 
> /Users/matthias/plt/racket/collects/racket/contract/private/blame.rkt:143:0: 
> raise-blame-error16
> >>>> > > /Users/matthias/plt/racket/collects/racket/private/misc.rkt:87:7
> >>>> > > > (read-object 'a 10 20)
> >>>> > > read-object: contract violation
> >>>> > > expected: (quote b)
> >>>> > > given: 'a
> >>>> > > in: the domain of
> >>>> > > the 2nd case of
> >>>> > > (case->
> >>>> > > (-> 'a number? number?)
> >>>> > > (-> 'b number? number? number?))
> >>>> > > contract from: 
> >>>> > > /Users/matthias/svn/2HtDP/foo.rkt
> >>>> > > blaming: top-level
> >>>> > > at: /Users/matthias/svn/2HtDP/foo.rkt:5.5
> >>>> > > context...:
> >>>> > > 
> /Users/matthias/plt/racket/collects/racket/contract/private/blame.rkt:143:0: 
> raise-blame-error16
> >>>> > > /Users/matthias/plt/racket/collects/racket/private/misc.rkt:87:7
> >>>> > 
> >>>> > 
> >>>> > I think you can use ->i contracts inside of the case-> clauses if you 
> need more precision. 
> >>>> > 
> >>>> > If I am still misunderstanding your question, sorry. 
> >>>> > 
> >>>> > -- Matthias
> >>>> > 
> >>>> > 
> >>>> > 
> >>>> > 
> >>>> > > > 
> >>>> > > > 
> >>>> > > > -- 
> >>>> > > > Roman Klochkov
> >>>> > > 
> >>>> > > 
> >>>> > > 
> >>>> > > -- 
> >>>> > > Roman Klochkov
> >>>> > 
> >>>> > 
> >>>> > 
> >>>> > -- 
> >>>> > Roman Klochkov
> >>>> 
> >>>> 
> >>>> 
> >>>> -- 
> >>>> Roman Klochkov
> >>>
> >>
> >>
> >>-- 
> >>Roman Klochkov
> >
> 
> 
> -- 
> Roman Klochkov
> ____________________
>   Racket Users list:
>   http://lists.racket-lang.org/users


Posted on the users mailing list.