Approaches for “Lazy” sequences #1668
Replies: 4 comments 10 replies
-
|
Beta Was this translation helpful? Give feedback.
-
Here are my initial thoughts: The term "sequence" brings swift's sequence protocol to my mind which I see as a kind of iterator factory. I think there are 2 kind of iterable entities:
I think these 2 entities are very independent and there is no need to couple it on a protocol level. Discussion at #1081 also goes in similar lines. Many times the term Any collections that can generate rvalues without copying can be handled by "PopFirstable" concept mentioned in #1081. We can provide default implementation of "Sequence" for all pop firstable collection. We might also consider better name than Sequence that somehow doesn't imply any connection with Collection. If we are on the same page for this, then for laziness we only need to worry about 2nd entity where notion of existence of multiple elements at a time exists. Then we can almost quite say that "multi-pass" and "positions" exist for lazy collections too. Regarding 2nd entity, we kind of know the properties of entity where it yields lvalue on access. rvalues on access is where laziness appears (like I am also unsure about the category of "views" which might return rvalues whose lifetime is dependent on lifetime of collection like |
Beta Was this translation helpful? Give feedback.
-
Here are some thoughts I have on laziness. Like @RishabhRD, I think there are two kinds of iterable entities, which I will call collections and streams. The former support multi-pass iteration and the latter supports single-pass iteration. I am not attached to those terms (or any of the other straw mans I'll use next) but I think they at least work for the sake of the discussion. I guess the basics are quite uncontroversial. For collections, we get start and end positions and a subscript to project (i.e., access an lvalue) contents at a specific position. For streams, we get something a mutating method that returns rvalues until the stream is depleted (if ever). For lazy collections, my working theory is that we can get away building on top of streams. For instance: var xs = Array(0 ..< 10)
var ys = xs.stream().filter(fun (e) { e % 2 == 0 })
print(&ys.take(3)) // prints '[0, 2, 4]'
&xs = ys.collect() // consumes the remainder of `ys` To make that work, It is easy to define a default implementation of I think we can get quite far with this approach. If I look at my own code, most applications of laziness are on collections that I'll throw away right after the lazy collection is used. However, as mentioned elsewhere, this approach applies poorly to cases where the original collection should not be consumed. One easy fix is to just copy the original collection before creating a stream (i.e., var xs = Array(0 ..< 10)
var ys = xs.lazy_map(Int.copy).filter(fun (e) { e % 2 == 0 })
print(&ys.take(3)) // prints '[0, 2, 4]'
&xs = ys.release() // assigns `xs` to its original value This trick only works if we can sink the original collection. That is where remote parts become tempting but I'd advocate for a higher-order function instead: let xs = Array(0 ..< 10)
xs.with_lazy_map(Int.copy, fun (_ zs : sink) {
var ys = zs.stream.filter(fun (e) { e % 2 == 0 })
print(&ys.take(3)) // prints '[0, 2, 4]'
}) |
Beta Was this translation helpful? Give feedback.
-
Survey of Laziness based on C++ ViewsI was looking into C++ "views" as views has reputation of "lazy" ranges. I think this might help to realize "different kind of laziness" possible and their relevance in collection model. I am listing down all the "views" we have currently in C++ and capabilities required for sensible operations on them. I have categorised 4 types of operations:
Another important factor is if MultiPass is required, then do view Returns Element or Projects Element or Element Access Depends on Parent. As returning element means element is computed "lazily" during element access. C++ Viewssingle_view, empty_viewThese are simple collections, I don't think there is something like "view" here.
-> Projects Element iotaLanguage features like
-> Returns Element NOTE: Unbounded overlaod is not considered as unbounded collections are not a real thing. repeat, cycle
-> Projects Element filterReordering with filter view would make parent collection state hard to reason about. I don't think there is any reasonable multi-pass search algorithm for filter not possible to implement with single pass nature.
transform/map
-> Returns Element take, drop, take_while, drop_whileCan be modelled with slices and split, chunk, slide, strideCan be seen as
zip, cartesian_product, adjacentIn reference based languages it looks like they return I also believe there should be a
-> Returns Element Here we see the problem of mixing Collection with LazyCollection while zipping. Does this imply unification, if so then how? I consider this view as a lazy collection as it computes the tuple. join (Monadic Join)
I see this would be best solved with segmentation which we are still exploring. Thus I would not comment much on it. Axes of LazinessThere are 2 axes of laziness I observed in C++ views:
Am I missing some axes here? We have seen that laziness required for computing positions are not required as So, computing elements is the only axis left for laziness.
Discussion on "element computation" LazyCollectionAssuming now with I see that every The situation is not so good in Rust. In rust, we lack yield once coroutine and I don't see rust community very enthusiastic for this feature. Then we have following options:
But Swift/Hylo model suggests to have another protocol named ConclusionI think looking into all views would be good way to discover different axes of laziness. This should help defining laziness in collections context. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
(See also #1081)
Since @RishabhRD is going to be exploring lazy sequences in rs-stl soon I thought it would be worth opening a discussion about the domain
I'd like to begin by outlining the roughly complete space of possible sequence concepts. Then we can decide which ones may or may not be important and how they should be represented (naming is also up for grabs as far as I'm concerned).
Dimensions of the space, assuming there is some notional sequence object
s
These are not necessarily supposed to be concepts or traits; they just represent properties of a sequence that might be important to some generic algorithm.
s
?s
?s
?Did I miss anything?
Beta Was this translation helpful? Give feedback.
All reactions