A variant on Alexis' example lets you circumvent Typed Racket's protections:
#lang racket
(module typed typed/racket
(provide wrapper use)
(struct wrapper ([v : Integer]))
(: use (-> wrapper Integer))
(define (use w)
(add1 (wrapper-v w))))
(require racket/runtime-path)
(define-runtime-module-path typed
(submod "." typed))
(define-values {wrapper use}
(parameterize ([current-inspector (make-inspector)])
(values (dynamic-require typed 'wrapper)
(dynamic-require typed 'use))))
(define unchecked-wrapper
(let-values ([(info skipped?)
(struct-info (wrapper 0))])
(struct-type-make-constructor info)))
(use (unchecked-wrapper 'nonsense))
Maybe this is well-known to everyone who's thought about it before, but I
hadn't. Though I guess, while I don't think about inspectors very often, I
think of them as a way for one module to put others under its control, in
which case maybe the module with the more powerful inspector *should* be
able to break subordinate modules' invariants.
-Philip
On Mon, Nov 5, 2018 at 9:01 PM Ryan Culpepper <[email protected]> wrote:
> On 11/5/18 5:26 PM, Alexis King wrote:
> > To my knowledge, there are two main techniques for creating unique
> values in Racket: `gensym` and structure type generativity. The former
> seems to be bulletproof — a value created with `gensym` will never be
> `equal?` to anything except itself – but the latter isn’t. Using reflective
> operations, it’s possible to circumvent the encapsulation afforded by the
> module system.
> >
> > To provide an example, `racket/contract` exports a value called
> `the-unsupplied-arg`, which is created using the usual structure type
> generativity trick:
> >
> > (define-struct the-unsupplied-arg ())
> > (define the-unsupplied-arg (make-the-unsupplied-arg))
> > (provide the-unsupplied-arg)
> >
> > The constructor is not exported, and this value is intended to be
> unique. However, if we can arrange for the contract library to be loaded on
> our terms, we can thwart this encapsulation by supplying it with a weaker
> inspector:
> >
> > #lang racket/base
> >
> > (define the-unsupplied-arg
> > (parameterize ([current-inspector (make-inspector)])
> > (dynamic-require 'racket/contract 'the-unsupplied-arg)))
> >
> > (define-values [info skipped?] (struct-info the-unsupplied-arg))
> >
> > (define another-unsupplied-arg ((struct-type-make-constructor info)))
> >
> > (equal? the-unsupplied-arg another-unsupplied-arg) ; => #t
> > (eq? the-unsupplied-arg another-unsupplied-arg) ; => #f
> >
> > Perhaps this isn’t the end of the world, as after all, we can’t do
> anything especially nefarious with this value, and we wouldn’t be able to
> do it in the first place if we didn’t have complete control of the
> inspector hierarchy. Still, it seems a little unsatisfying.
> >
> > So, my question: is there any way to create a structure type for which
> there can truly ever only be one instance? If not, is there a fundamental
> reason why not?
>
> You could use a chaperone to prohibit `struct-info`:
>
> (define the-unsupplied-arg
> (chaperone-struct (make-the-unsupplied-arg)
> struct:unsupplied-arg
> struct-info (lambda _ (error "not allowed"))))
>
> Ryan
>
> --
> 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 [email protected].
> 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 [email protected].
For more options, visit https://groups.google.com/d/optout.