Skip to content
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

Implement TaskSeq.skipWhile, skipWhileAsync, skipWhileInclusive and skipWhileInclusiveAsync #219

Merged
merged 13 commits into from
Dec 22, 2023

Conversation

abelbraaksma
Copy link
Member

@abelbraaksma abelbraaksma commented Dec 21, 2023

Fixes #130 (edit: see also #235)

Part of the push for implementing low-hanging from as listed in #208. Implements the following signatures:

static member skipWhile: predicate: ('T -> bool) -> source: TaskSeq<'T> -> TaskSeq<'T>
static member skipWhileAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> TaskSeq<'T>
static member skipWhileInclusive: predicate: ('T -> bool) -> source: TaskSeq<'T> -> TaskSeq<'T>
static member skipWhileInclusiveAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> TaskSeq<'T>

Note that inclusive here means that it skips at least one item (i.e., the modifier applies to the action of skipping). This is analogous to how takeWhileInclusive etc work.

Some explanation may be warranted for future reference. Here's a truth table of 1..10 and a filter function of filter x = x < 5 for both inclusive and non-inclusive skip functions (also applies to take functions):

skipWhile

// truth table for f(x) = x < 5
// 1 2 3 4 5 6 7 8 9 10
// T T T T F F F F F F (stops at first F)
// x x x x _ _ _ _ _ _ (skips exclusive)

> [1..10] |> TaskSeq.ofList |> TaskSeq.skipWhile (fun x -> x < 5);;
val it: TaskSeq<int> = taskSeq [5; 6; 7; 8; 9; 10]

skipWhileInclusive

// truth table for f(x) = x < 5
// 1 2 3 4 5 6 7 8 9 10
// T T T T F F F F F F (stops at first F)
// x x x x x _ _ _ _ _ (skips inclusive)

> [1..10] |> TaskSeq.ofList |> TaskSeq.skipWhile (fun x -> x < 5);;
val it: TaskSeq<int> = taskSeq [6; 7; 8; 9; 10]

TODO list:

  • Implement TaskSeq.skipWhile
  • Implement TaskSeq.skipWhileAsync
  • Implement TaskSeq.skipWhileInclusive
  • Implement TaskSeq.skipWhileInclusiveAsync

@abelbraaksma abelbraaksma force-pushed the implement-skipwhile-and-variants branch from b9d62a6 to 0b25ccd Compare December 21, 2023 01:44
@abelbraaksma
Copy link
Member Author

@bartelink, in case you have some time, I can use an extra pair of eyes here (meanwhile, I'll fix the failing tests).

@abelbraaksma abelbraaksma force-pushed the implement-skipwhile-and-variants branch from 3cfecd3 to 8567f34 Compare December 21, 2023 14:13
@abelbraaksma abelbraaksma force-pushed the implement-skipwhile-and-variants branch from 7af6d9a to 17aa445 Compare December 21, 2023 14:27
@abelbraaksma abelbraaksma force-pushed the implement-skipwhile-and-variants branch from 7802df6 to 0aa0742 Compare December 21, 2023 14:50
@abelbraaksma
Copy link
Member Author

@bartelink thanks for the extensive review, much appreciated! I applied all your comments, insofar they were still applicable after some other changes.

@abelbraaksma abelbraaksma added topic: surface area Adds functions to the public surface area feature request New feature or enhancement request labels Dec 21, 2023
@abelbraaksma
Copy link
Member Author

Almost forgot to update the readmes and release notes, oops!

@bartelink
Copy link
Member

re that use case...
there's a double iteration in the error case
and its actually the last item returned that would be the failing one
but if there was only one item in the list then that last item would be valid

the example you have feels more like you are picturing a scan until or something

if you want to skip while some condition and then skip one more, that's not going to help you show the extra one you skipped

is there some partition function of some kind that this might make sense as? such a thing could split a list into 3 over some pivot or something?

@bartelink
Copy link
Member

for takeWhile, I can explain the use case more easily...

I am searching until I find something
if I reach the end of the list, then that's fine too
but I also want the final item that I was searching for
so its a 'scanUntil' semantic, which AsyncSeq/Eirik happened to call takeWhileInclusive as its literally TakeWhile but includes the last item

It does not particularly lend itself to showing the failing one, as the final item can either simply be the last item in the list
or it can be the item that defines the place where we abort the search

In completely concrete terms, I'm searching backward through a list of events until I meet either a) a Snapshot b) a Reset event or c) the end of the list
Once I have that part of the list, I can fold/reduce from there forward

@abelbraaksma
Copy link
Member Author

Oh yeah, I deleted my "use case" shortly after posting it. But here's one:

  • you have a sequence of the form A*B;C*D;..., but without the tuples. That is, every item is a pair, but it is a flat sequence
  • you want to "skip while", on each first item
  • you can do some windowing, but you could also just use skipWhileInclusive

Yep, a bit contrived, for sure. I'm sure other use cases will come up, or not...

@bartelink
Copy link
Member

so you have a list with pairs of items and when you see the first one, you want to skip its partner
that's not very general though - what if the pairs that are not pairs that you are explaining as pairs were triples?

ultimately the reason why takeWhileInclusive does make sense is that it's all about the single item being tested - do you want the final one or not

also not wanting the one after that you're not even going to look at before discarding implicitly is not a logical inverse of that

(See above about windowing/partitioning/having a pivot item - SkipWhileInclusive is ultimately very baroque and I'm not seeing any real example - maybe if you can find one in the wild (or any prior art / discussion of this logical duality), that would help?)

@abelbraaksma
Copy link
Member Author

maybe if you can find one in the wild (or any prior art / discussion of this logical duality), that would help?)

Yeah, that's gonna be hard. Just like with takeWhileInclusive, which I cannot find "in the wild" on a quick search, I doubt I can find its logical inverse, skipWhileInclusive. Let's keep it in, as not doing so would raise questions on takeWhileInclusive. If we want the one, we should want the other as well (parity, orthogonality, principle of least surprise etc).

@abelbraaksma abelbraaksma merged commit 06bc268 into main Dec 22, 2023
6 checks passed
@abelbraaksma abelbraaksma deleted the implement-skipwhile-and-variants branch December 22, 2023 00:18
@abelbraaksma
Copy link
Member Author

Some things I missed from review comments go in #220.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request New feature or enhancement request topic: surface area Adds functions to the public surface area
Projects
None yet
2 participants