-
Notifications
You must be signed in to change notification settings - Fork 107
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
Replace Var newtype with GADT to increase power in update callbacks #260
base: master
Are you sure you want to change the base?
Conversation
I really could use this update! I keep having to choose between saving extra useless information in my model and putting enough in the |
I'll take another look at it |
I thought this was a bit odd when I first saw it. Essentially, the difference this provides is that one will be able to pattern match and inspect the values during This could lead to some very odd bugs, where you're running commands and expectations on states which shouldn't support them. Do you have a motivating case or short code snippet which demonstrates your problem? |
For example, I've got this exec function for a
|
I'm having a think about this. It's a bit surprising to me that server's response when starting a session wouldn't change an expectation you might have down the line. If the HTTP request fails, isn't that important to how your application works, the information you expect the server to know (there's a new active new session), and the properties of the system you expect to hold (the session is active till I close it)? If it's not important, then either your model seems inadequate in that assumes that sessions don't actually need to exist on the server at all; or they actually don't need to exist on the server, and you might as well just not make the HTTP request. This doesn't sound realistic. To help model this, I would: a) consider the UUID to be an input, and have it as part of the generated command (this way it's available for the model update anyway); or |
That request returns no data. Its only purpose is to tell the server the unique identifier that will be used for subsequent analytics calls and to let the server annotate that identifier with the request's meta data. That is why the What I'm trying to show is that there are elements you return from your exec function that are just for Notes:
|
I'll make more systematic comments later, but for now, I still think using the UUID as part of the input is a better way to go. You can generate one with |
At first using I've got a possibly better example of the
The The unrelated issue is if the way I'm getting a random subset of data I don't have at compile time is going to break shrinking or if there is a better way to randomly choose parts of data generated/fetched by commands. |
I would again posit that this should be modeled as input.
I don't think this should be the case, no? This would appear to mean that your model for the system doesn't accurately capture some of its important concepts. I do understand now what you're asking for (I don't think it's this PR). I believe that what you want is for a Functor constraint to also be revealed to the update function. Like this: Update (forall v. (Ord1 v, Functor v) => state v -> input v -> Var output v -> state v) which would allow you to map over both symbolic and concrete outputs, and place either one or more projections of them into the state. I could get this to type check, the issue though, is that it wouldn't actually work.
If we had two differently typed values in there, but they were both derived from the same output, they would still hold the same name. We couldn't differentiate them when attempting to reify, nor turn them into correctly typed values. Sorry to be repetitive, but I think you probably are reaching for this because you have a mismatch between what should be an input to your program and what should be an output from an execution. |
I agree that there isn't a meaningful way of splitting out what you want in your model and what you only wanted to test. What I am curious about is this mismatched between what should be an input and what should be an output from an execution. If you will excuse this off topic discussion, here is the crux of the issue. Each API call, beyond user setup, is dependent on dynamic data from previous API calls. Now, I could copy down the current state of some of these endpoints and use them as inputs or write generators for all posible values, like you describe for the search options, but that would mean my As an example: If I made the search options an input that I generated locally that would test creating a valid search from any set of search options, but not the search options that are actually on the API. Ideally I should have both, but the latter is more important for making sure what is there now will work as expected. Making the UUID in the input instead of the output is a different story. It makes sense for it to be an input, but I'm don't know how to adjust the shrinking to never re-use the same UUID. So, my question to you is why do you think a model for the system doesn't accurately capture some of its important concepts if it has to get them dynamically (as a Edit: I hope I have interpreted your words correctly, let me know if I've misunderstood what you are advocating. I'm also asking this because I'm doubting the way I am writing my |
4139585
to
c228279
Compare
This PR replaces the
newtype
Var
with a version using GADTs. This increases power in the update callback, where one must provide a state updateforall v
. Previously if I had an outputVar (Int, String) v
, I would not be able to construct aVar String v
, whereas now I can:An extension of this would convert the
Symbolic
andConcrete
types to nullary types, reducing them to tags. This would remove the need for two layers of constructors inVar
s. I wanted to submit the PR without the extension to reduce breaking changes to the API. In this version the only breaking change is the change to theVar
constructor.