-
-
Notifications
You must be signed in to change notification settings - Fork 647
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Add equal-always? #4076
WIP: Add equal-always? #4076
Conversation
6560698
to
dc4e41a
Compare
043c1fd
to
497747c
Compare
Why does that one stress test in |
The test that's hanging is It should be starting 30 places, each with a thread that spawns a future which may or may not loop, and each place may or may not custodian-shutdown-all. or am I wrong about how printing works? |
4938d47
to
ec621b8
Compare
Side note: there's another branch on my fork called |
I labeled the commit It seems to be passing the CI for everything except building Racket BC on ARM64... but on ARM64 the error seems nasty |
On the topic of how to express custom I think some common cases should be made more succinct:
It sounds like the default behavior of Structure type property guards can check whether the structure type's info has immutable fields and determine different property values based on that, so even if Hmm, I doubt the |
Hmm, found a potential issue in how I specified Interpreting Perhaps the right way to resolve this is to say that every structure type with mutable fields automatically behaves as though it has |
I've been thinking about circumstances where one operation would dispatch to the other, and now I regret what I was just proposing. If a type doesn't specify But in the world we're in now, where types customize This aspect of If "equal always" needs to do "equal now" sometimes for legacy reasons, and if it's natural for "equal now" to treat "equal always" as a fallback, then perhaps they should be one combined interface rather than dispatching back and forth. So I'm basically at square one. I'm sure I'll eventually have another set of opinions and another suggested design that takes this into account, but I should probably not get carried away until some other people have expressed the way they think about this. |
Yes that's a problem, a (struct bxwrp (box)
#:methods gen:equal+hash
[;; implement one layer of equal-now explicitly:
(define (equal-proc self other =?)
(=? (unbox (bxwrp-box self)) (unbox (bxwrp-box other))))
(define (hash-proc self rec) (rec (unbox (bxwrp-box self))))
(define (hash2-proc self rec) (rec (unbox (bxwrp-box self))))])
(chaperone-of? (bxwrp (box 1)) (bxwrp (box 1))) ;=> #true but ideal should be #false
(equal-always? (bxwrp (box 1)) (bxwrp (box 1))) ;=> #true but ideal should be #false Because of this I'm at least glad that by default (struct bxwrp (box) #:mutable
#:methods gen:equal+hash
[;; implement one layer of equal-now explicitly:
(define (equal-proc self other =?)
(=? (unbox (bxwrp-box self)) (unbox (bxwrp-box other))))
(define (hash-proc self rec) (rec (unbox (bxwrp-box self))))
(define (hash2-proc self rec) (rec (unbox (bxwrp-box self))))])
(chaperone-of? (bxwrp (box 1)) (bxwrp (box 1))) ;=> #false just like we want
(equal-always? (bxwrp (box 1)) (bxwrp (box 1))) ;=> #false just like we want With your original suggestion the user could alternatively fix it by adding However, |
My opinions are still recovering from their self-defeat :) but I want to point out that this kind of data structure seems like it could just as well be implemented so that It seems like the differences only arise with subtyping, and the only time I've particularly needed struct subtyping is to let two structures with different sets of interfaces or different sets of fields be |
7e126bd
to
d193932
Compare
I've added a test for the "honest" variant of |
a872f00
to
b0bdeba
Compare
77b5840
to
8218c45
Compare
The CI is failing on
This diverges from what a passing run should look like here:
What does this mean? Update:The part of the error message In that same class The function I can take a guess that it might have happened in (1) under I still don't know where the other part of the error message |
I'll take a look. |
I haven't been able to replicate the crash seen for Windows in CI. I'll keep investigating, but I think it makes sense to assume for now that it's unrelated to this PR's changes. |
Overall, the implementation here seems in very good shape. The code changes look right to the best of my ability to read the. The changes cover almost everything I could think of, and many things that I didn't immediately think of. The only additional change I thought of: updating ad hoc optimizations in "optimize.c" and schemify, especially ones that convert Maybe the only implementation piece to fill in is some |
@rocketnia and I were discussing this in the comments above starting with Nia's comment "On the topic of how to express custom equal-always? behavior." In particular, someone writing a struct that uses mutation and wants There is one case where a struct is "technically" mutable but "conceptually" immutable, and that's with Lazy data structures. I don't know of any existing Racket lazy data structures that implement |
My understanding from the Rhombus meeting is that we're already pretty sure that we'll want that, so it seems like we should sort it out now rather than later, just in case doing so reveals some issue. |
The idea of "equal always" having to dispatch to "equal now" for legacy reasons had me unmotivated about this for a while, but I think I've come up with something. We don't have to think of this as a flaw in the I think one of the overriding concerns for the design of a new method (or a whole new interface) is going to be performance, especially making sure the interfaces aren't doing a lot of unnecessary dynamic dispatch to each other in normal use. For performance's sake, I suspect a good design for the new |
Oh I hadn't realized |
equalw is short for EQUal ALWays, first 3 letters of each word
522cf50
to
9c94eb6
Compare
9c94eb6
to
480eb48
Compare
I recently started suggesting on the Rhombus equality thread that for more conventional arithmetic semantics, Rhombus's That got me back to thinking about this
What if more than one It could be reasonable to add documentation to That documentation change would be something of a break in compatibility by telling users a new requirement they hadn't heard of before. However, the real introduction of this new requirement happened whenever The issue I was thinking of at the time is actually slightly different than this (struct bxwrp3 (box)
#:methods gen:equal+hash
[(define (equal-proc self other =?)
(equal? (bxwrp3-box self) (bxwrp3-box other)))
(define (hash-proc self rec) (rec (bxwrp3-box self)))
(define (hash2-proc self rec) (rec (bxwrp3-box self)))])
(chaperone-of? (bxwrp3 (box 1)) (bxwrp3 (box 1))) ;=> #true Fortunately, this code can be fixed by calling the provided In summary, I think my concerns with Helping people avoid the hazard is good too. I started to talk about that above with "a new method [or interface] could be added that [...] allowed the defaults to flow better," and in early February, we had a bunch of discussion about the details of this in the Rhombus #equality channel on the Racket Discord. I don't recall exactly where we ended up with that. Maybe some of the details might still depend on how equality and ordered comparison would work in Rhombus? |
Closing in favor of #4236 |
This pull request has been mentioned on Racket Discussions. There might be relevant details there: |
Add
equal-always?
, defining it such that(equal-always? a b)
is true iff there exists a valuev
such that botha
andb
are chaperones ofv
.It uses extensional equality for immutable data structures, and intensional equality for mutable data structures.
I still need to do several things before it would be ready, including:
chaperone-of?
on mutable data are good, document them.equal-always?
.equal-always?
equal-always?
,equal-always?/recur
, and the associated hash-codes, hash-tables, sets, etc.prop:equal-always+hash
prop:equal-always+hash
prop:equal+hash
andgen:equal+hash
, document what it means for an implementation to be "honest" and work properly withchaperone-of?
andequal-always?
, vs "dishonest".