Re: [racket-users] Contracts for generic interfaces (or struct type properties?)

2017-05-24 Thread Philip McGrath
I was interested after getting your reply in what racket/dict does, it it
seems to get blamed for bad implementations of the dict-set method:

(struct bad-dict ()
  #:methods gen:dict
  [(define (dict-set this k v)
 'not-a-new-dict)])

(dict-set (bad-dict) 'a #t)

dict-set: broke its own contract
  promised: dict?
  produced: 'not-a-new-dict
  in: the _r result of
  (->i
   ((d (dict-implements/c dict-set))
(k (d) (dict-key-contract d))
(value (d) (dict-value-contract d)))
   (_r dict?))
  contract from: /racket/dict.rkt
  blaming: /racket/dict.rkt
   (assuming the contract is correct)
  at: /racket/dict.rkt:190.2


I understand the reasons you explain for why blame is being assigned the
way it is, but my intuition before I encountered this was that the module
defining a generic interface could specify a contract for a generic method
and make the module supplying the actual implementation responsible for
satisfying the range part of the contract. In terms of boundaries, I guess
I wanted a boundry between the caller of a generic method and the
implementor of the method, where the contract could be specified by the
author of the interface without taking responsiblity for bad third-party
implementations. It looks like racket/dict wanted to write a contract like
that, too.

The class system's interfaces specify contracts of the kind I had in mind.
For example:

#lang racket

(module string-server racket
  (provide can-be-string<%>)
  (define can-be-string<%>
(interface ()
  [to-string (->m string?)])))
(module buggy racket
  (require (submod ".." string-server))
  (provide buggy%)
  (define buggy%
(class* object% (can-be-string<%>)
  (super-new)
  (define/public (to-string)
'not-a-string
(require (submod "." buggy))
(send (new buggy%) to-string)


blames buggy% in the error message (though it blames the class, not the
module, so this is presumably a different sort of boundry).

I think support for this sort of boundry would be a valuable addition to
the generics library.

-Philip

On Tue, May 23, 2017 at 10:31 AM, Vincent St-Amour <
stamo...@eecs.northwestern.edu> wrote:

> Hi Philip,
>
> I don't think you can express the contract boundary you have in mind
> using the generics library as it is.
>
> The blame you see for both contracts makes sense. In the first case,
> `string-server` is not protecting itself from bad inputs (uses `any/c`)
> yet promises to return a string. It rightly gets blamed when that
> doesn't happen.
>
> In the second case, `string-server` does protect itself (using
> `can-be-string/c`), but it protects itself against its callers, who now
> have the responsibility to pass it something reasonable. It doesn't
> (passes the `buggy` struct along), so it rightly gets blamed.
>
> To get the contract boundary that you want, I think you'd want to wrap
> the `buggy` constructor with a contract that makes it commit to
> producing a valid `can-be-string`. You could do this either when you
> export it from the `buggy` module, or by "laundering" it through a
> submodule before getting it to the main module. That way, it should end
> up being blamed.
>
> This is not a great solution, because you'd have to do this for every
> instance of the generic interface, but the class of boundaries you have
> in mind cannot currently be described, I think.
>
> Vincent
>
>
>
>
> On Mon, 22 May 2017 22:31:25 -0500,
> Philip McGrath wrote:
> >
> > I'm running into trouble in trying to give contracts to methods from
> generic interfaces (in the sense of racket/generic) that assign blame to
> the implementor of a bad method implementation. This seems like it's
> probably a common problem, so I'm
> > hoping a solution exists.
> >
> > For an example, consider the following:
> >
> >  #lang racket
> >
> >  (module string-server racket
> >  (require racket/generic)
> >  (provide gen:can-be-string
> >  can-be-string?
> >  (contract-out
> >  [to-string
> >  (-> any/c
> >  string?)]
> >  ))
> >  (define-generics can-be-string
> >  (to-string can-be-string)
> >  #:fast-defaults
> >  ([string?
> >  (define (to-string str)
> >  str)])))
> >
> >  (module buggy racket
> >  (require (submod ".." string-server))
> >  (provide (struct-out buggy))
> >  (struct buggy ()
> >  #:methods gen:can-be-string
> >  [(define (to-string this)
> >  'not-a-string)]))
> >
> >  (require (submod "." string-server)
> >  (submod "." buggy))
> >
> >  (to-string (buggy))
> >
> > This program raises an exception as expected, but blames the
> string-server module rather than the buggy module.
> >
> > The string-server module can deflect blame from itself by changing the
> contract of to-string to
> > (-> (can-be-string/c [to-string (-> any/c string?)]) string?), but the
> result is even worse: now the error blames the enclosing module.
> >
> > The closest thing I've found to a solution is to abandon racket/generic
> and use a struct type property with a guard that performs lots of checks
> 

Re: [racket-users] Contracts for generic interfaces (or struct type properties?)

2017-05-23 Thread Vincent St-Amour
Hi Philip,

I don't think you can express the contract boundary you have in mind
using the generics library as it is.

The blame you see for both contracts makes sense. In the first case,
`string-server` is not protecting itself from bad inputs (uses `any/c`)
yet promises to return a string. It rightly gets blamed when that
doesn't happen.

In the second case, `string-server` does protect itself (using
`can-be-string/c`), but it protects itself against its callers, who now
have the responsibility to pass it something reasonable. It doesn't
(passes the `buggy` struct along), so it rightly gets blamed.

To get the contract boundary that you want, I think you'd want to wrap
the `buggy` constructor with a contract that makes it commit to
producing a valid `can-be-string`. You could do this either when you
export it from the `buggy` module, or by "laundering" it through a
submodule before getting it to the main module. That way, it should end
up being blamed.

This is not a great solution, because you'd have to do this for every
instance of the generic interface, but the class of boundaries you have
in mind cannot currently be described, I think.

Vincent




On Mon, 22 May 2017 22:31:25 -0500,
Philip McGrath wrote:
> 
> I'm running into trouble in trying to give contracts to methods from generic 
> interfaces (in the sense of racket/generic) that assign blame to the 
> implementor of a bad method implementation. This seems like it's probably a 
> common problem, so I'm
> hoping a solution exists.
> 
> For an example, consider the following:
> 
>  #lang racket
> 
>  (module string-server racket
>  (require racket/generic)
>  (provide gen:can-be-string
>  can-be-string?
>  (contract-out
>  [to-string
>  (-> any/c 
>  string?)]
>  ))
>  (define-generics can-be-string
>  (to-string can-be-string)
>  #:fast-defaults
>  ([string?
>  (define (to-string str)
>  str)])))
> 
>  (module buggy racket
>  (require (submod ".." string-server))
>  (provide (struct-out buggy))
>  (struct buggy ()
>  #:methods gen:can-be-string
>  [(define (to-string this)
>  'not-a-string)]))
> 
>  (require (submod "." string-server)
>  (submod "." buggy))
> 
>  (to-string (buggy))
> 
> This program raises an exception as expected, but blames the string-server 
> module rather than the buggy module.
> 
> The string-server module can deflect blame from itself by changing the 
> contract of to-string to
> (-> (can-be-string/c [to-string (-> any/c string?)]) string?), but the result 
> is even worse: now the error blames the enclosing module.
> 
> The closest thing I've found to a solution is to abandon racket/generic and 
> use a struct type property with a guard that performs lots of checks and 
> eventually calls chaperone-procedure. Aside from being verbose and 
> error-prone (in that it requires
> manually writing checks correctly rather than relying the contract system), 
> it also doesn't really address the problem of blame, since errors from e.g. 
> raise-arguments-error don't blame anything in particular.
> 
> Is there a way to write the kind of contract I have in mind?
> 
> Thanks,
> Philip
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to racket-users+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


[racket-users] Contracts for generic interfaces (or struct type properties?)

2017-05-22 Thread Philip McGrath
I'm running into trouble in trying to give contracts to methods from
generic interfaces (in the sense of racket/generic) that assign blame to
the implementor of a bad method implementation. This seems like it's
probably a common problem, so I'm hoping a solution exists.

For an example, consider the following:

#lang racket

(module string-server racket
  (require racket/generic)
  (provide gen:can-be-string
   can-be-string?
   (contract-out
[to-string
 (-> any/c
 string?)]
))
  (define-generics can-be-string
(to-string can-be-string)
#:fast-defaults
([string?
  (define (to-string str)
str)])))

(module buggy racket
  (require (submod ".." string-server))
  (provide (struct-out buggy))
  (struct buggy ()
#:methods gen:can-be-string
[(define (to-string this)
   'not-a-string)]))

(require (submod "." string-server)
 (submod "." buggy))

(to-string (buggy))


This program raises an exception as expected, but blames the string-server
module rather than the buggy module.

The string-server module can deflect blame from itself by changing the
contract of to-string to
(-> (can-be-string/c [to-string (-> any/c string?)]) string?),  but the
result is even worse: now the error blames the enclosing module.

The closest thing I've found to a solution is to abandon racket/generic and
use a struct type property with a guard that performs lots of checks and
eventually calls chaperone-procedure. Aside from being verbose and
error-prone (in that it requires manually writing checks correctly rather
than relying the contract system), it also doesn't really address the
problem of blame, since errors from e.g. raise-arguments-error don't blame
anything in particular.

Is there a way to write the kind of contract I have in mind?

Thanks,
Philip

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.