diff --git a/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj b/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj
index dbf1cfc..240ef00 100644
--- a/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj
+++ b/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj
@@ -19,6 +19,7 @@
+
diff --git a/src/FSharp.Control.TaskSeq.Test/TaskSeq.DistinctUntilChanged.Tests.fs b/src/FSharp.Control.TaskSeq.Test/TaskSeq.DistinctUntilChanged.Tests.fs
new file mode 100644
index 0000000..e26e50d
--- /dev/null
+++ b/src/FSharp.Control.TaskSeq.Test/TaskSeq.DistinctUntilChanged.Tests.fs
@@ -0,0 +1,39 @@
+module TaskSeq.Tests.DistinctUntilChanged
+
+open Xunit
+open FsUnit.Xunit
+
+open FSharp.Control
+
+//
+// TaskSeq.distinctUntilChanged
+//
+
+
+module EmptySeq =
+ []
+ let ``TaskSeq-distinctUntilChanged with null source raises`` () = assertNullArg <| fun () -> TaskSeq.distinctUntilChanged null
+
+ [)>]
+ let ``TaskSeq-distinctUntilChanged has no effect`` variant = task {
+ do!
+ Gen.getEmptyVariant variant
+ |> TaskSeq.distinctUntilChanged
+ |> TaskSeq.toListAsync
+ |> Task.map (List.isEmpty >> should be True)
+ }
+
+module Functionality =
+ []
+ let ``TaskSeq-distinctUntilChanged should return no consecutive duplicates`` () = task {
+ let ts =
+ [ 'A'; 'A'; 'B'; 'Z'; 'C'; 'C'; 'Z'; 'C'; 'D'; 'D'; 'D'; 'Z' ]
+ |> TaskSeq.ofList
+
+ let! xs = ts |> TaskSeq.distinctUntilChanged |> TaskSeq.toListAsync
+
+ xs
+ |> List.map string
+ |> String.concat ""
+ |> should equal "ABZCZCDZ"
+ }
diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fs b/src/FSharp.Control.TaskSeq/TaskSeq.fs
index 710dadd..32c159f 100644
--- a/src/FSharp.Control.TaskSeq/TaskSeq.fs
+++ b/src/FSharp.Control.TaskSeq/TaskSeq.fs
@@ -358,6 +358,8 @@ type TaskSeq private () =
static member except itemsToExclude source = Internal.except itemsToExclude source
static member exceptOfSeq itemsToExclude source = Internal.exceptOfSeq itemsToExclude source
+ static member distinctUntilChanged source = Internal.distinctUntilChanged source
+
static member forall predicate source = Internal.forall (Predicate predicate) source
static member forallAsync predicate source = Internal.forall (PredicateAsync predicate) source
diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fsi b/src/FSharp.Control.TaskSeq/TaskSeq.fsi
index ff98586..76e90c2 100644
--- a/src/FSharp.Control.TaskSeq/TaskSeq.fsi
+++ b/src/FSharp.Control.TaskSeq/TaskSeq.fsi
@@ -1297,6 +1297,16 @@ type TaskSeq =
/// Thrown when either of the two input task sequences is null.
static member exceptOfSeq<'T when 'T: equality> : itemsToExclude: seq<'T> -> source: TaskSeq<'T> -> TaskSeq<'T>
+ ///
+ /// Returns a new task sequence without consecutive duplicate elements.
+ ///
+ ///
+ /// The input task sequence whose consecutive duplicates will be removed.
+ /// A sequence without consecutive duplicates elements.
+ ///
+ /// Thrown when the input task sequences is null.
+ static member distinctUntilChanged<'T when 'T: equality> : source: TaskSeq<'T> -> TaskSeq<'T>
+
///
/// Combines the two task sequences into a new task sequence of pairs. The two sequences need not have equal lengths:
/// when one sequence is exhausted any remaining elements in the other sequence are ignored.
diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs
index 66a92f2..2509a28 100644
--- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs
+++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs
@@ -1097,3 +1097,22 @@ module internal TaskSeqInternal =
go <- step
}
+
+ let distinctUntilChanged (source: TaskSeq<_>) =
+ checkNonNull (nameof source) source
+
+ taskSeq {
+ let mutable maybePrevious = ValueNone
+
+ for current in source do
+ match maybePrevious with
+ | ValueNone ->
+ yield current
+ maybePrevious <- ValueSome current
+ | ValueSome previous ->
+ if previous = current then
+ () // skip
+ else
+ yield current
+ maybePrevious <- ValueSome current
+ }