-
Notifications
You must be signed in to change notification settings - Fork 409
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
Variants - Simplified #1859
Comments
Hi ! (most of the work is done in this commit: TheLortex@92de8f6) |
This default variant thing is something that should be discussed separately I think. I'm not yet sure it makes sense over a default implementation. Consider two virtual libraries setting a conflicting default "variant". How would the default variant be selected for an executable depending on those libraries? |
BTW, one thing I had in mind: currently we can force a particular implementation by adding it as a dependency. In some cases, implementations might be added through transitive dependencies. Now, an implementation might come from a dependency on an implementation that was automatically selected through a variant. At best, we'd get conflicting implementations, but at worse we might not get the expected result. I believe what I'm trying to say is that I'm not sure the selection process is confluent. I.e. depending in the order in which the code make choices, we might not get the same final set of libraries. Which is not very satisfying. |
It might be worth reasoning on an abstract version of the algorithm, so that we can convince ourselves it is correct. |
Yes after thinking about it it's not an interesting feature. Having a default implementation sounds better. Regarding the algorithm. Mine is currently quite wrong so I agree on reasoning about an abstract version ! My idea was something like this:
Forcing implementations over the whole dependency tree seems tricky, what we can try is forcing implementations down the children of a node (an extended version of user written deps). |
To illustrate the issue when having both default implementations and variant forcing for the whole tree, with the following dependency scheme:
If we resolve |
Do you have an example in mind of how could that happen?
The way I frame the problem, there should only be a unique way to select the implementations based on a set of variants. So the case where an implementation is pulled transitively and a different implementation is forced via a variant is an error. Although perhaps we should allow for explicitly overriding stuff like that. |
@TheLortex Do you want to work on the default implementation issue separately? I believe it should be quite a bit simpler and required nonetheless. It will get you up to speed with the code base. Did you have a look at my algorithm as well? I explain that you cannot just do the closure and the variant selection separately.
There shouldn't be a need for this because the final step in our pipeline validates the closure. That is, if you insert the variant selection step before this final step, you will not have to worry about constructing invalid closures. You'll only have to worry about giving a good error message so the user knows where an implementation is being pulled from. |
The problem with default implementation is the same I believe. We were just discussing this with @aalekseyev who came up with the following example: black arrows are normal dependencies and orange arrows are default implementations. Depending on which orange arrow we follow first, we get different implementations for Async and Clock. The only way is to indeed follow all possibilities and report an error afterwards when we have too many implementations for the same virtual library. The algorithm proposed by @TheLortex looks right, just a couple of comment:
This should include the transitive deps of user written deps, since this is what we are currently doing.
Here I assume that we only follow implementations that were newly added through variants or default implementation selection. @aalekseyev suggested the following idea: we can consider that we associate a state to every existing virtual library. The state can be one of:
and we update the state of virtual libraries as we follow the graph. I believe that's roughly the same as merging the independent transitive closure, however it might be slightly faster. |
I feel like it's not that simple, according to @diml example..
Yes I did, and what I wanted to say is that we can't choose an implementation directly when a virtual library is added to the closure. We might need more information in order to know if the virtual library is already implemented or not, right ? @diml |
I believe the right behaviour for the example is to fail and ask the user to write additional stuff in order to help dune decide what to do. I don't really see a way around exploring all possibilities, but technically it doesn't seem too bad: it's just one additional edge for each virtual library to traverse. |
The new algorithm takes place in closure computation. It builds a map from virtual libraries to implementations, and resolves default implementation selection. New tests have been added.
I've been able to implement default implementations + variants and it seems to work alright with my hand made cases. Error messages need to be more user-friendly. |
Yes, please make a PR. |
The new algorithm takes place in closure computation. It builds a map from virtual libraries to implementations, and resolves default implementation selection. New tests have been added. Signed-off-by: Lucas Pluvinage <[email protected]>
The new algorithm takes place in closure computation. It builds a map from virtual libraries to implementations, and resolves default implementation selection. New tests have been added. Signed-off-by: Lucas Pluvinage <[email protected]>
The new algorithm takes place in closure computation. It builds a map from virtual libraries to implementations, and resolves default implementation selection. New tests have been added. Signed-off-by: Lucas Pluvinage <[email protected]>
The new algorithm takes place in closure computation. It builds a map from virtual libraries to implementations, and resolves default implementation selection. New tests have been added. Signed-off-by: Lucas Pluvinage <[email protected]>
This proposal has been implemented but it turns out to be flawed. We have a new proposal (#2134) to address the issues introduced by this one. |
Variants - Simplified
(This proposal is aimed at @TheLortex who has expressed some interest in implementing this feature. However, some details need to be hashed between the maintainers first)
Now that we've merged virtual libraries, we can consider implementing the full
variants proposal. It's heavily simplified from the original proposal so I'm
rewriting the original ticket.
TL;DR:
Variants are now tags that are attached to implementations. Executables may now
select groups of implementations simultaneously by choosing such tags.
Implementation Steps
Modify the library stanza to allow marking library implementations with an
optional
(variant ..)
field.Modify the executables stanza to allow for a
(variants ..)
field. It shouldallow for a list of tags to be selected.
Modify the
dune-pacakge
format accordingly to maintain variant informationfor implementations.
When loading the library database, we must maintain a map from individual
variants
to a map of virtual libraries to implementations. The next stepwill make it obvious why we need such a data structure.
When calculating the closure for linking (executables), we now need to select
implementations based on the set of tags. I'll describe this in a bit more
detail
Roughly, the closure pipeline for executables would look like:
In
...
, we'd like to insert a step that would insert implementations based onthe selected set of variants.
So essentially:
Now there's a bit of a problem here: we must calculate the closure again after
this step, and recursively do (
1
;2
) until2
. doesn't change the closure.This is because an implementation itself may depend on a virtual library.
Consider:
Now, if we calculate the closure of
x
we'll discoverfoo
and that we needthe
foo_js
library. But now we'll also need to select the correctimplementation for
bar.
Taking the closure + finding implementations in a fixpoint doesn't seem very
attractive, so unless @diml has a better idea, I suggest we interleave the
variant-based implementation selection with the closure itself. In this new
closure, we'll check if the current library is virtual, and we have an
implementation for it based on the set of variants present. In this case, we'll
just add the correct implementation to the closure directly.
Possible Issues & Future Work
As usual, making decent error messages always increases the difficulty. Let's
worry about this once we have a prototype ready. We can threw some test cases
and improve errors from there.
Since variant tags is going to be quite small (compared to the set of
libraries at least), We might need a conflict resolution mechanism when two
libraries provide an implementation for the same library with the same variant
name. Yes, in this case the user can always just drop variants altogether and
go back to concrete implementations, but that's a bit dissapointing.
Perhaps one could resolve this ambiguity by providing a concrete
implementation in the
libraries
field for the executable. So when doinglookups from
4
, we'll keep in mind the list of libraries the user actuallylisted.
The text was updated successfully, but these errors were encountered: