-
Notifications
You must be signed in to change notification settings - Fork 11
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
Change IsLambda* trait structure #48
Comments
Also, the reason I started looking into this change is that I was looking at #46. With this change to |
Sorry I've taken so long to reply. Just been terribly busy. There is a good reason for the In the "Denotational Semantics": There is an extra method only found in the Haskell version of sodium. So sodium Streams are like a superset of a functor, which has the addition of permitting mutation within a lambda through something like A special use case is squeezing more performance out of collections. Instead of having collections being updated in pseudo code example:
In the example above. cEntities can perform an update to the collection in There are a few more bugs I've discovered in this version that I have not had time to fix. I've been doing an alternative C++ version as well with fully automatic memory management implemented the same way as this Rust version: https://github.com/clinuxrulz/sodium-cxx-alt . And while doing that I've discovered bugs I'd expect to be in this Rust version too as the implementation is identical. The C++ version has the tests copied over from the original C++ version where the memory management is not quite right yet, and the C++ version had a few more tests that are missing from the Rust version that I'd expect to fail. And I'd really like the tests in a single file too like "sodium_test.rs". It did not make sense to break down the tests in separate files like I've done. And then go through the C++ version code and add the missing tests to eliminate the remaining bugs. Although the sodium objects hold a lazy value, that value should be evaluated/unboxed at the end of transaction to insure any side effects get executed in the transaction even if not listened too. ( I can see in your fork you've done a few other changes too, they can be useful here and I'd like to merge some of it. If you'd like to help me out some more and have the free time, I'd like some more of the tests from https://github.com/clinuxrulz/sodium-cxx-alt/blob/master/tests/test_sodium.cpp added to this Rust version. I remember clinuxrulz/sodium-cxx-alt@549da61 Basically I was not meant to use post, but instead send the current value of the Any additions to the exposed API will bump up the 2nd number in the version |
Ah, that makes sense! I guess if I'm going to work on Sodium I should probably read and understand that "Denotational Semantics" appendix. I would be happy to help out, I'm definitely interested in using sodium-rust for a few projects and I would love to help out. I'll separate out the other fixes on my branch from the lambda change and submit those as a pull request and take a look at porting and re-organizing the tests. |
I submitted a couple PRs with conceptually distinct sets of changes I've made on my fork. If you don't mind, I think I will also take your above comments and make issues for each of them to track the work on each individually. I also have been thinking about the usage of This change would also be in keeping with Rust's general design ethic of being safe/immutable by default but allowing mutation, or unsafety escape hatches with more explicit syntax. I think there could be room for adding a new helper function to facilitate the boilerplate related to using this pattern for mutable update. As you say, that would definitely be a breaking change to the API so I'm not in a rush to make it. I also think there is another breaking change that could result in a significant improvement in using Sodium. I believe the It looks like part of the reason for having the I think related to this change, there is another benefit to using the |
I'm happy for you to add additional issues. Might just need a little more thought for the inference. Rust's iterators from the std handle type inference on FnMut with no problems and they can process references to each of the elements too instead of copy of element passed to closure. The next node in the graph receiving a immutable reference to the collection in the mutual collection example is for safety. It's like the one node in the graph takes ownership of the mutable state of that collection. And each node in the graph should only be able to mutate its own state then afterwards the state should remain unchanged for the lifetime of the transaction so all other dependent nodes all see the same state for that transaction. Accepting a non reference type in the input to a lambda would cause a copy of the collection defeating the O(1) making it O(n) due to the copying for dependent nodes to witness the new value of the collection. It seems it is the dependency tracking causing the issue with the type inference. Maybe |
One question just came to mind: Will Rust let us implement |
I just realised since my lambdas are returning a non-reference type, I'd have to use So I'd be happy to go from I'd also be not too fazed if lambdas accepted non-referenced types as inputs (especially if it helps with type inference). As using references can often cause a cache miss on the CPU. And if the user knew they had a structure that was expensive to copy, then they can always wrap it in a |
ooh, that is a very good question. perhaps not. another thing that would need to be experimented with. |
Another option to keep dependency tracking and type inference is to make twins for all the API methods. E.g.
Just a bit painful with all the boilerplate. |
Hmmm, boilerplate can be solved with a macro, but I wonder how much complexity calling the two methods would add internally? I'm discovering that this is a substantially harder problem to solve than a naively thought when I first posted (in hindsight this probably should not have been a surprise). I have a few other directions that might pan out, but I think I may have to seek from more experienced Rustaceans on whether it's possible to do what I'm aiming for and how to implement it cleanly. It might also be the case that the issue with type inference is not coming from the trait indirection of the |
Have not had a chance to get on a computer yet. Just stuck on mobile phone. I wonder if hinting to the compiler a variable is a reference would help the type inference. E.g.
Notice the Edit: tested on Termux Linux terminal for Android. The Have a look at the https://doc.rust-lang.org/src/alloc/boxed.rs.html#1020-1024 So maybe we can define |
https://blog.rust-lang.org/2019/05/23/Rust-1.35.0.html Actually looks like it is a stable feature. We can implement What is interesting is: |
I think that the change in 1.35 is actually just implementing the relevant traits for boxed versions of the various |
Thanks for clearing that up. Just have to experiment a bit. Having the twin methods as a option is no big deal internally. It's just delegating the "non with deps" method calls to the "with dep" ones carrying an empty |
I had a crazy idea once, not sure how workable it is. The idea is to execute |
This works: It infers the type of It means type inference should work fully (no type hints), if the IsLambda did not use |
Oh, that's really good news. I didn't even think to try that abbreviated inferencing! I think in general since most values are already passed by reference removing the And if in 3.0 we change the |
Without the It's really passed by move, but since we want to keep the value/state in the sodium object we have no choice but to copy/clone. Making it pass by copy for the arguments.
The only real difference between |
https://docs.rs/serde_closure_derive/0.3.1/src/serde_closure_derive/lib.rs.html Shows how to create a macro that can visit the captured variables in a closure. And it works on stable. So it should be possible to create a E.g. And Or even a Maybe no need for this technique just yet. The example from serde seems quite complex. Worried if it would keep working as the Rust language changes over time. |
Ah, you are totally right. I got a little overexcited about that idea and didn't really think it through. I think my thoughts on this API change are pretty half-baked right now, and not really grounded in how sodium is actually implemented currently or what the goals are, though this discussion has certainly been making things clearer. I think I'll keep working on porting tests and fixing any bugs that they expose in the implementation and getting more experience working in the codebase that way. |
I think it would make more sense to bound all the
IsLambda*
traits withFn
instead ofFnMut
. I have tried out this change on my fork and it seems to be pretty straightforward to do.For reference, the Fn trait requires that any variables a
Fn
is closed over must be immutable references, thereby putting up a barrier to mutation.While usage of any type that grants "interior mutability" will allow a user to get around this limitation and break referential transparency, by using the
Fn
trait we provide at least a safety check that the user isn't accidentally mutating something.There are a few places I believe where
IsLambda
is now being used that actually should allow mutation (anything in the operational side of things I would imagine), and so I think it would make sense to also add correspondingIsLambdaMut
andIsLambdaOnce
traits to mirror the trio ofFn*
traits.The text was updated successfully, but these errors were encountered: