rokitna
2020-10-4 07:57:59

I think struct/contract is probably the main way to do it.

Far more detail: That way, I think the procedures people see still satisfy struct-accessor-procedure? etc., and the constructor’s binding is still associated with static information about the structure type. This information lets people set up impersonators and declare inheriting structure types, which are both good to expose if you want people to be able to define contract combinators that recognize and refine instances of your type. (Only subtypes of your type can have instances equal? to your type’s instances, and contracts generally try to preserve equal? even as they augment the value with error-checking.)

That said, at first I found it pretty surprising I was exposing all those capabilities from my libraries. I’ve gone to some trouble to hide that information behind plain procedures that I provide with contract-out. I feel like it’s cost me a lot in terms of verbosity compared to just using struct-out.


greg
2020-10-4 15:43:58

I think one “meta” observation from this discussion is that Racket is flexible enough to support pretty much whatever you want to do. You can decide where the contracts are placed. Independently, you can decide how to organize your code (depending on your preference for proximity of definitions, contracts, tests, even documentation).


greg
2020-10-4 15:44:31

So I mean it’s good to have general advice, especially for people doing “general-purpose” programs and/or just learning Racket. That’s super helpful!


greg
2020-10-4 15:44:52

And each project and/or team should agree on some conventions, for sanity and consistency.


greg
2020-10-4 15:45:39

But ultimately it’s a decision you or your team or your community make, as opposed to something dictated by Racket.


greg
2020-10-4 15:47:42

Also, whatever approach you choose, with macros you can calibrate this between super explicit and repetitive “housekeeping” code at one extreme, and super DRY and concise and “magical” at the other extreme, or anywhere in between. :smile:


greg
2020-10-4 15:49:39

feels “best practice” is usually less about computer science and math, and usually much more about “how do a bunch of primates cooperate in a way that is productive and healthy”.


samth
2020-10-4 16:55:46

@kellysmith12.21 you can put contracts on structs with contract-out


rokitna
2020-10-4 19:21:38

(oh, that’s right! I had seen that feature of contract-out before and forgot about it)


yilin.wei10
2020-10-4 20:46:32

Hi all,

I’m getting a problem with ffi/unsafe when calling out to a foreign function using the (_ptr o ...) construct. The signature is not particularly interesting - it’s simply a (_fun _pointer (_ptr o my-struct) -> _void) where my-struct is defined by define-cstruct but I’m getting the following contract violation from the code generated by _fun.

ptr-ref: contract violation expected: cpointer? given: #<thread-resume-evt> argument position: 1st other arguments...: #<ctype> context...:

If I do it “manually”, the following signature, returns a #<thread-resume-evt> instead of the expected _cpointer type.

(_fun _physical-device [properties : _? = (malloc _physical-device-properties)] [_pointer = properties] -> _void -> properties) I’m at a loss at how to debug this; are there any resources which I could use to try and narrow down the problem? I’m on Racket 7.5, BC (I think that’s the default for 7.5?).


yilin.wei10
2020-10-4 22:01:18

Upgrading to 7.8 didn’t help - the output is still the same


yilin.wei10
2020-10-4 23:32:59

After poking around it further - I think it’s an issue with my code not defining certain structs correctly - I think that the cstruct definition is incorrect (some fields should be fixed-length arrays). Mucking around with cstruct gives a different type. I’m surprised this is the outcome though.


mflatt
2020-10-5 00:02:00

If you give the foreign function a pointer to memory that is allocated too small, then pretty much anything can happen. For example, the first two bytes of a nearby allocated object might get set to 128 and 0, which would make the object look like a thread-resume-evt on a little-endian platform.