
What takes forever to build?

Is there a standard way to combine dynamic-require
with a strong make-security-guard
? I want to deny as much as possible, but dynamic-require still needs to use 'exists
and 'read
, but I’d like the code (apart from the requires) to not access the file system at all. Or is a sandbox the right tool here? (but I don’t want an evaluator, just a dynamic-require)

A sandbox does solve various problems with loading code, such as allowing read access to installed collections. But a sandbox might get in the way if you want the loaded code to use modules that you’ve already instantiated in a namespace.

That may be a problem indeed. I guess I’ll go with allowing ’exists and ’read. Since I disable network access, that may be sufficient

I’m adding contracts to a module that worked before without contracts. Now, when I add a contract to a struct sort-spec
(defined as (sort-spec (order field) #:transparent)
), I get an error from another module that requires the module with the contracts: task-group.rkt:20:24: struct: parent struct type not defined;
identifier does not name struct type information
at: sort-spec
in: (struct task-group-spec sort-spec (tag-key) #:transparent)
context...:
/home/.../collects/racket/private/define-struct.rkt:153:2
/home/.../collects/syntax/wrap-modbeg.rkt:46:4
The contract I added for sort-spec
is (contract-out [sort-spec (-> symbol? symbol? sort-spec?)])
The child struct task-group-spec
is defined as (task-group-spec sort-spec (tag-key) #:transparent)
.
I wonder what’s going on here and how to fix it?

Can you give a program that results in an error?

Let me try to make a simple program …

This program is enough: #lang racket/base
(require
"task.rkt"
"task-group.rkt")
The code of the modules (without the contracts) is here: https://git.sr.ht/~sschwarzer/todoreport-racket/tree/main/item/task.rkt https://git.sr.ht/~sschwarzer/todoreport-racket/tree/main/item/task-group.rkt

The provide
section with the contracts is this: (provide
(contract-out
[sort-spec (-> symbol? symbol? sort-spec?)]
[sort-spec-field (-> sort-spec? symbol?)]
[sort-tasks (-> (listof task?) (listof sort-spec?) (listof task?))]
[string->task (-> string? task?)]
[string->tasks (-> string? (listof task?))]
[task->string (-> task? string?)]
[task-field (-> task? (or/c symbol? sort-spec?) any/c)]
[tasks->string (-> (listof task?) string?)])
;sort-spec
sort-spec?
value-sort-func
)

The error occurred after adding the contract for sort-spec
.

This is the first time I define contracts, so this might be a beginner mistake. :slightly_smiling_face:

My guess, from just a glance, is that, while you’re providing sort-spec
as a function (a constructor of the struct), you’re not providing the struct info itself.

@jaz How would I provide the struct info?

When you said:
> The provide section with the contracts is this: What file are you adding the contracts to?

task.rkt?

To task.rkt

Yes

OK, I can reproduce the error. I need to download an old version of Racket to test if it has always been an error like this. My fear is that I might have introduced a bug when I changed contract-out
at some point.

In case it helps, I use Racket 8.0 cs.

@sorawee Isn’t it just that, when the contract is added, this clause: [sort-spec (-> symbol? symbol? sort-spec?)]
just provides the contractor and not the struct type transformer?

Which has been a pain point (at least for me) for a long time.

I don’t know! And even if it does, it doesn’t look like @sschwarzer uses the compile-time binding of sort-spec
anywhere else…

The use is in task-group.rkt
, where sort-spec
is the parent struct type of task-group-spec
. If that file is only importing the constructor, then the struct definition won’t work.

@jaz I guess by contractor you mean constructor? Can I provide the struct type transformer (whatever this is :wink:) somehow as a workaround?

Sorry — yes, that was a typo.

Ah, indeed

The easiest way to handle this case (which will work if you don’t need to make the struct type transformer public) would be to create two separate modules — one that exports the sort-spec
struct without contracts and one that exports the contracted definitions that you want. Then, task-group.rkt
would require the former.

@jaz Then it’s probably easier for now to leave out the contract for sort-spec
for now. :slightly_smiling_face:

I think using things like struct/contract
will also work


Oh, good point.

struct-guard/c
can also be used.

@jaz Is the struct type transformer the macro that converts the struct definition as written in the source code to the constructor, predicate, accessors etc.?

There’s some info about it here: https://docs.racket-lang.org/reference/structinfo.html?q=struct

So, when you (provide sort-spec)
, you are providing two things at once:
- The constructor
sort-spec
- The information about
sort-spec
at compile-time.

In your:
(struct task-group-spec sort-spec
; `tag-key` is a string for the tag key, say, "due". `#f` if `field` isn't a
; 'tags field.
(tag-key)
#:transparent)
you are using the information of sort-spec
at compile-time.

So there are a couple of ways to fix this problem.

@sorawee struct/contract
for defining sort-spec
works.

I assume the contract boundary in that case is the constructor/accessor/mutator itself, right?

Meaning that the contract would be enforced even within the defining module.

Yep

@jaz As for define/contract
, right?

Yep

Whereas, when you use provide/contract
you make the module the contract boundary.

Oh oh oh

@sorawee In case you enter a ticket on Github, can you give me the id, so I can add the URL to a comment? Also, if you want to mention me on Github, my Github account is also sschwarzer .

I completely forgot that contract-out
supports struct

It does, but does it allow opacity/partial opacity?

Just because I want to export the constructor does not mean I want to export the accessors.

That’s a good point.

I simply thought that the constructor behaves like a function and would follow the same rules when using contract-out
. :slightly_smiling_face:

(In my case, when I want to export the struct type info, it’s for match expansion reasons.)

I’ve run into this same issue many times over.

@jaz Yes, about the accessors. I may only want to use them internally in the module where the struct is defined.

Just to finish what I was typing before:
Another possibility is that, you can explicitly decouple the compile-time information of sort-spec
from the constructor sort-spec
. This can be done by specifying an option to struct
(I believe it’s #:name
but I could be wrong) and provide both. Then, change the task-group-spec
definition to use the new compile-time information.

@sorawee @jaz Just for info: struct/contract
doesn’t offer all the arguments as struct
(according to the documentation), so it’s not always a usable workaround (apart from where the contracts are enforced). For my particular case, struct/contract
should be ok for now.

Actually, I’d advise against struct/contract
. I forgot that the struct
clause in contract-out
exists. That would be a better choice for your program, I think.

@sorawee Ah, I see, in https://docs.racket-lang.org/guide/contracts-struct.html . I hadn’t read this part of the docs yet or forgot it. :slightly_smiling_face: So I’ll try to use this.
Does that mean that contract-out
isn’t supposed to be used with struct constructors (used as a function, like I did) to begin with?

Yeah, that’s the part that I am not sure. Perhaps contract-out
could be smarter and reattach the compile-time information.

I just tested against Racket 7.7, which is the version before I modified the interaction between contract-out
and structs. The program fails similarly, so I think it has always been failing.

I had read the contract documentation in the Racket Guide when I started with Racket, but at that time I had very little actual experience and forgot most of the chapter. Now I’m adding contracts and was reading the Guide again and applying it.

@sorawee So maybe the limitation should be documented (if it isn’t already and I missed it)? On the other hand, if it’s not too difficult to export the compile-time info, this should of course be easier for users. On the other hand, if that’s tricky it maybe better to stick with the API specifically for structs.

@sorawee @jaz Thanks for your help. :+1:

(require racket/contract)
(provide
(contract-out
[sort-tasks (-> (listof task?) (listof sort-spec?) (listof task?))]
[string->task (-> string? task?)]
[string->tasks (-> string? (listof task?))]
[task->string (-> task? string?)]
[task-field (-> task? (or/c symbol? sort-spec?) any/c)]
[tasks->string (-> (listof task?) string?)])
value-sort-func
(rename-out [sort-spec-contracted sort-spec]
[sort-spec-field-contracted sort-spec-field]))
(module sort-spec racket/base
(provide (struct-out sort-spec))
(struct sort-spec
(; 'asc for ascending order, 'desc for descending order
order
; Task field to sort by, say, 'creation-date
field)
#:transparent))
(module sort-spec-contracted racket/base
(require racket/contract
(submod ".." sort-spec))
(provide (contract-out (struct sort-spec ([order symbol?] [field symbol?])))))
(require
'sort-spec
(only-in 'sort-spec-contracted
[sort-spec sort-spec-contracted]
[sort-spec-field sort-spec-field-contracted])
racket/list
racket/match
racket/port
racket/string)

Here’s an approach that @jaz mentioned earlier, I believe, and this will be the closest to what you intended to do earlier.

The submodule sort-spec
simply provides sort-spec
uncontracted, so that the main module can use sort-spec
uncontracted.
The submodule sort-spec-contracted
adds full contracts to sort-spec
.
The main module has an access to both the contracted and uncontracted versions. You can use uncontracted version internally in the module, but when you want to provide them out, you use the contracted version, and you can do so partially (don’t need to provide all accessors)

Racket should definitely provide a syntactic construct that helps with this process.

@sorawee Far too much code for what I would like to have. :wink: I’ll have a look at the struct section in the contracts documentation and use that.

If the documented contracts struct stuff doesn’t work with inheritance either, I can still fall back to not using contracts for the struct constructor.

If all you want to do is provide the constructor with the struct info but not one of the accessors then struct-out plus except-out should be pretty simple

Unfortunately, except-out
doesn’t seem to work with contract-out
.

Also, would be nice to have only-out

> Unfortunately, except-out doesn’t seem to work with contract-out. Seems to be due to how the contracted id is not free-id=? with the exception ids.

Is there a naming convention for Racket to denote a name is used only internally in a module? (I know that the visibility to clients is controlled by provide
, but I would like to make the internal use for some names also visible in these names.)

I’ve never seen private bindings spelled differently, in Racket.

You could borrow the preceding underscore from Python: my-public-function
and _my-private-function
. (or, I suppose you could make that a preceding dash, since Racket is traditionally kebab-case)

Or maybe a /private
suffix (or other term)?

That makes it clear what you mean, so that’s great.

I wonder if I understand the struct
form in contract-out
. My provide
is (provide
(contract-out
;[sort-spec (-> symbol? symbol? sort-spec?)]
[sort-spec-field (-> sort-spec? symbol?)]
[sort-tasks (-> (listof task?) (listof sort-spec?) (listof task?))]
[string->task (-> string? task?)]
[string->tasks (-> string? (listof task?))]
[task->string (-> task? string?)]
[task-field (-> task? (or/c symbol? sort-spec?) any/c)]
[tasks->string (-> (listof task?) string?)]
(struct sort-spec (order symbol?) (field symbol?)))
value-sort-func
)
(although only the (struct sort-spec ...)
should be interesting here.
Further down in the module, sort-spec
is defined as ; Order-symbol Task-field-symbol -> Sort-spec
(struct sort-spec
(; 'asc for ascending order, 'desc for descending order
order
; Task field to sort by, say, 'creation-date
field)
#:transparent)
When requiring the module, I get the message task.rkt:18:23: contract-out: malformed struct field
at: order
in: (contract-out (sort-spec-field (-> sort-spec? symbol?)) (sort-tasks (-> (listof task?) (listof sort-spec?) (listof task?))) (string->task (-> string? task?)) (string->tasks (-> string? (listof task?))) (task->string (-> task? string?)) (task-field (-> t...
context...:
/home/.../collects/racket/contract/private/provide.rkt:596:14: try-next
/home/.../collects/racket/contract/private/provide.rkt:548:0: true-provide/contract
/home/.../collects/racket/contract/private/out.rkt:11:3
/home/.../collects/racket/provide-transform.rkt:65:2: pre-expand-export
/home/.../collects/racket/private/reqprov.rkt:732:2
/home/.../collects/syntax/wrap-modbeg.rkt:46:4

You need another parenthesis

(struct sort-spec ((order symbol?) (field symbol?)))

Thanks, that’s better. :slightly_smiling_face: Now I get /home/.../collects/racket/contract/private/provide.rkt:382:8: module: identifier already defined
at: provide/contract-id-sort-spec-field
in: (define-syntaxes (provide/contract-id-sort-spec-field) (make-provide/contract-arrow-transformer (quote-syntax provide/contract-id-sort-spec-field) (quote-syntax idZ66) (quote-syntax sort-spec-field) (quote-syntax idX64) (quote-syntax idY65) #s(valid-app...

Perhaps comment out [sort-spec-field (-> sort-spec? symbol?)]
?

Yes, that works. :+1: My understanding is that this should be implied by the struct
in contract-out
?


I’m using drRacket. Anyone know why the windows 10 version racket 8.0 not working when testing images ?

This is a known issue that was fixed already


So you can either downgrade to 7.9 or download a snapshot version (http://snapshot.racket-lang.org/) if you can’t wait for a new version.

thanks so uninstall/reinstall 7.9 huh.

@samth what I wanted to say was that taking a minimal Racket and then installing the handful of packages I need. They end up pulling in a lot of dependencies, so I end up fetching and building a lot of what you find in a full Racket install anyway.