-
Notifications
You must be signed in to change notification settings - Fork 177
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 close semantics for fixed-length sources #239
Conversation
I would like more people to review this proposal, if willing. Comparisons with, and experience from, Async are welcome. What this change doesRelevant commit: 6bc3263 This change "eagerly" pre-allocates the whole internal stream queue for streams created with The existing functions are "lazy," in the sense that if you do So, there is a performance effect. As I understand it, this change would simplify some part of Cohttp specifically. What the obstacle isTL;DR: The proposed change is not general enough. For example, it seems wrong that these will evaluate to Lwt_stream.is_closed @@ Lwt_stream.of_list @@ List.map (fun x -> x) l
Lwt_stream.is_closed @@ Lwt_stream.map (fun x -> x) @@ Lwt_stream.of_list l And, as mentioned, it seems wrong that The change harms the regularity, and teachability, of Lwt's interface. However, it can be acceptable as is, if we can see it as "on the way" to a larger change that restores regularity. We don't have to implement the larger change – just, if someone requests, e.g., How to make it more general
|
I looked at Async pipes and they do what is proposed in this PR. i.e. What I had in mind when I first wrote I heard several times that people prefer Async pipes to Lwt streams, so maybe this PR is a step in the right direction. At the same time I'd be slightly worried as it changes the semantic without notice for users, which might lead to hard to debug problems. Another solution would be to add a |
Thanks @diml!
Do you remember the reason(s), or perhaps there are links somewhere?
This behavior of
in particular? |
Ok, I see two ways forward with this PR, and I prefer (2).
@seliopou I hope (2) is suitable for what you need. If you agree, please make the history of this PR into one commit with the implementation, optionally with tests (in the same commit). I would also appreciate if you capitalized the first letter of the message for consistency with recent history. EDIT: and please rebase over |
There is no reason to expect that those two operations should commute when lifted to a monad. As @diml pointed out, let main () =
let p1 = Pipe.of_list [1; 2; 3] in
print_endline (string_of_bool (Pipe.is_closed p1));
let p2 = Pipe.map p1 ~f:(fun x -> x + 1) in
print_endline (string_of_bool (Pipe.is_closed p2));
Clock.after (Time.Span.of_sec 1.0) >>> fun () ->
print_endline (string_of_bool (Pipe.is_closed p2));; ...will produce the output:
If you change the
In the first case, it does not report a closed state unless values in the pipe are consumed. This is because Fundamentally, the debate is about whether An equally-valid implementation of let of_list l =
let stream, push = Lwt_stream.create () in
List.iter (fun x -> push (Some x)) l;
push None;
stream Using I don't think that option (2) is the right way to go because it leaves the interface to this module in a worse state than it was before, both to understand, to teach, and to maintain in the future. |
I disagree with several statements above, but this paraphrase convinced me: " Perhaps the docs for Please squash along the lines mentioned above. If you'd rather have me squash, let me know. I will post some branch and you can review and replace this PR with it. |
Previously, the of_list, of_array, and of_string constructors each would produce a stream s for which is_closed s = false This is counter-intuitive as all the elements of the stream s have been generated and should be consumable without blocking. This commit changes the behavior of this constructors such that is_closed s = true immediately upon creation.
This is ready to be merged. |
let b = ref false in | ||
let is_closed_in_notification = ref false in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this should be removed. I think is_closed
should be true inside threads waiting on closed
, and it is conceivable that some future implementation might get that wrong.
Attaching a termination callback to an already closed stream should result in the callback being invoked.
The test now consists of checking that the stream is not closed when initially constructed, and that once all its elements are consumed, the closed thread has returned, and the stream in fact has been closed.
let b6 = Lwt_stream.(is_closed (of_string "123")) in | ||
let b7 = Lwt_stream.(is_closed (from_direct (fun () -> Some 1))) in | ||
let b8 = Lwt_stream.(is_closed (from_direct (fun () -> None))) in | ||
return (b1 && b2 && b3 && b4 && b5 && b6 && not b7 && not b8)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing this no longer tests, is that streams for which is_closed
is false
can be turned into streams for which it is true
, by reading them.
Apart from the nit above, looks good. Please squash (in part to avoid twiddling some of the lines in |
I will not be spending any more time on this pull request. |
Childish. |
Please either update your PR so that
or allow me, as suggested, to cherry-pick or make amendments, subject to your review – which I am quite willing to do. I can/will make the suggested documentation edit. Technical objections and discussion welcome. |
If I were revoking my contribution, I would have closed the PR myself with a note stating as much. As did already state, I'm not going to commit any more effort to getting these merged. Merge if you wish, however you wish, as long as attribution is retained. |
Thanks, that's very thoughtful. I hope you don't think that I closed your PR because I thought you were revoking your contribution. I closed it because it is not suitable for merging as is. So, without your explicit permission to rewrite, and with you unwilling to amend, there was nothing further to do. I thought that was clear. I am the more so careful about your permission, since you have expressed to me, on IRC in the past, that you dislike PR history being rewritten under any circumstances. So, thanks for making yourself clear in this instance.
I'll take a look later at whether I can amend this in such a way, that it remains easy to use with both blame and bisect, and still retain exact attribution. I will try to avoid it, but I may have to tweak a few lines in the For comparison, your commits, as they are now, would make several lines written by me look in blame as if they were written by you, though they haven't changed at all (and it wouldn't be easy to trace them back to their original insertion due to them being deleted and re-inserted). The authorship part of that doesn't bother me, and I hope you can offer a similar level of flexibility in this situation. |
This pull request includes a change to the close semantics of fixed-length sources that was previously included in #223, but requires further discussion. An extensive discussion in IRC took place between myself and @aantron, which can be found here.
In summary, changing
of_list
andof_array
to be closed by default is likely an acceptable choice, but the library should accommodate similar functionality for other sort of structures. Right now, this could be accomplished with a function of the following form:However, that may not be at all obvious to users.
The motivation for this change is to allow libraries that consume a user-constructed stream to be able to determine the blocking behavior of a stream and use appropriate serialization strategies. As of now, there is no way in general to determine whether a stream will block in order to generate its elements, or if all its elements have been generated and can be read immediately. In the case of
of_list
andof_array
, this is information is known when the constructors are called, but is then discarded. This pull request would not discard that information.Depends on #223.