Skip to content

Commit

Permalink
Seq extensions: added tryHeadTail
Browse files Browse the repository at this point in the history
FSharpLint's new recently added rule[1] complains about
function Seq.tail being a partial function, so we need
an alternative before all try-prefixed versions get
included in FSharp.Core[2]. This is not actually a
tryTail function but at least most uses of Seq.tail
use a Seq.head before it, so we can replace both calls
to Seq.(try)head & Seq.tail with a single-call to this
Seq.tryHeadTail which shouldn't be considered a partial
function anymore (even if it calls Seq.tail underneath).

Adding it in FSharpx.Collections would be a nice stopgap
instead of having to include this function in every F#
project that wants to enable this new FSharpLint rule.

[1] fsprojects/FSharpLint#453
[2] fsharp/fslang-suggestions#803
  • Loading branch information
su8898 authored and knocte committed Sep 17, 2021
1 parent dec2f3b commit fdbeaf2
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/FSharpx.Collections/Collections.fs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ module Seq =
then None
else Some(LanguagePrimitives.DivideByInt< (^a) > acc count)

let tryHeadTail<'T> (sequence: seq<'T>): Option<'T * seq<'T>> =
match Seq.tryHead sequence with
| None -> None
| Some head -> Some (head, Seq.tail sequence)

/// Splits a sequences at the given index
let splitAt n seq = (Seq.take n seq, Seq.skip n seq)
Expand Down
14 changes: 14 additions & 0 deletions tests/FSharpx.Collections.Tests/SeqTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,20 @@ module SeqTests =
data |> Seq.tryAverage
|> Expect.equal "tryAverage" (Some (5.5)) }

test "If I tryHeadTail and I don't have a head, I should return None" {
Seq.empty<float> |> Seq.tryHeadTail
|> Expect.isNone "tryHeadTail" }

test "If I tryHeadTail a non-empty seq, I should return both head and tail" {
let data = [1; 2; 3]
let actual = data |> Seq.tryHeadTail
Expect.isSome "tryHeadTail" actual
match actual with
| Some (head, tail) ->
Expect.equal "tryHeadTail" 1 head
Expect.sequenceEqual "tryHeadTail" [2; 3] tail
| _ -> failwith "Unreachable" }

test "I should be a to split a seq at an index" {
let (a,b) = Seq.splitAt 5 data
Expect.sequenceEqual "splitAt" (List.toSeq [1.;2.;3.;4.;5.]) a
Expand Down

0 comments on commit fdbeaf2

Please sign in to comment.