-
Notifications
You must be signed in to change notification settings - Fork 54
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
Don't copy imported module to filter out its dependencies #776
Conversation
ModuleSpace doesn't keep imported spaces as atoms. It keeps them in a separate collection instead. This allows constructing new instances of the ModuleSpace without filtering original imported space.
|
Should also update the |
I think, at the conceptual level, we both hit upon the same solution; so our thinking is very well aligned. My thoughts were written up in the |
I have conflicting opinions on the question of "is it correct"? On the downside, this is relying on implementation details of RefCell, so I think it would not technically be considered sound. For example, if RefCell implemented a layer of internal indirection this could break. Also, this approach would have a race condition if we changed the RefCell to a Mutex or RwLock in the future. I am sad because, in a perfect world, this should not require unsafe. I wish there were a I did not find a crate that already does this, but I have wished for one myself in the past. (in fact, this is why I left the method unimplemented for DynSpace earlier in the year.) If I had a few spare days I would try and generalize this pattern to a stand-alone crate and get it code-reviewed by the wider community, but I don't have the spare time to do that right now.
|
UPDATE on the DynSpace implementation of Space::atom_iter Sadly, it also looks like owning_ref (another possible solution) still has one remaining soundness hole as well. https://users.rust-lang.org/t/whats-the-current-state-of-the-owning-ref-crate/102098 |
Ugh. I realized that there is an unfixable* safety hole in the implementation of Say the user borrows an iterator from the space behind a RefCell, then uses the iterator to get references to some atoms, saves those references, and then drops the iterator. The user could then use To make this safe, the lifetime of the returned references can't outlive the iterator itself. So you'd need a method like |
For |
Thanks Luke, it took a while to realize it but after reading |
What bother me here is that using |
In practice, I think this concern is slightly tempered by the realization that iterating every atom in a space is only a viable solution for relatively small spaces. So copying the atoms might not be so bad. For large spaces, iterating the whole space is a non-starter, with or without a copy. So you could return
In Mork, the approach we are taking is that the outcome of a space-wide operation is another space. And it's rarely necessary to iterate a space. Although it is possible. Essentially it's the fold idea taken all the way. So this is very compatible with our thinking. Hopefully we can start sketching out how MORK fits in, this December.
This actually bothered me too. This is what I meant by matryoshka nesting spaces, when we were talking about the general DynSpace approach a year ago. I think your solution is good. But to be honest I might go even further. (if not immediately, then eventually, as time permits) I'm personally not a fan of the pattern of a smart pointer (DynSpace in this case) implementing the same interface as the thing it points to. I think it's useful for a client to know if they have a space itself or a shared smart pointer to a space. For example, the abstract |
At the moment contents of the imported module are copied to filter out its dependencies. It makes modules depend heavily on some way of iterating through the space. Bigger downside is that it duplicates the space on import and makes import slower as mentioned in Luke's comment in the code.
In this PR
ModuleSpace
is introduced which keeps main space and its dependencies separately. This allows querying them separately and when dependent space is queried it doesn't queried recursively. Thus all transitive dependencies are queried only once from the top level space.For example if we have space
A
which imports spaceB
andC
andD
is imported fromB
andC
thenA
containsB
,C
andD
as dependencies in a separate collection. Query toA
goes through its atoms first then goes toB
,C
andD
but dependencies of these spaces are not queried.ModuleSpace::atom_iter()
iterates through main atomspace only. This breaks tests inf1_imports.metta
which are disabled in this change. There are two ways of solving this: (1) adding dependency spaces as atoms intoModuleSpace
iterator which resembles previous behavior, (2) add separate function which gets dependencies from space. I would like to implement solution after discussing a way.Second big change in this PR is implementation of the
Space::atom_iter
forDynSpace
. This change was necessary because nowModuleSpace
is a top space and it containsDynSpace
as a main space. When client getsatom_iter
fromModuleSpace
the call is forwarded toDynSpace
which returnsNone
. Any non-standard combination of theDynSpace
and other spaces can fall into this issue. Thus I decided to implement it despite it cannot be implemented withoutunsafe
.I am raising this as a draft PR to discuss two questions.
Space::atom_iter
implementation forDynSpace
I implemented. Is it correct? Do you think it is needed?ModuleSpace
as a way of solving dependency duplication issue?