From f7497d8f158c0e48811f6c5bd2af392c0a80af86 Mon Sep 17 00:00:00 2001 From: gusty <1261319+gusty@users.noreply.github.com> Date: Thu, 29 May 2025 09:19:44 +0200 Subject: [PATCH 1/3] Fix comment: this function is not in FSharp.Core --- src/FSharpPlus/Extensions/List.fs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/FSharpPlus/Extensions/List.fs b/src/FSharpPlus/Extensions/List.fs index 316c24725..98549879a 100644 --- a/src/FSharpPlus/Extensions/List.fs +++ b/src/FSharpPlus/Extensions/List.fs @@ -30,8 +30,7 @@ module List = /// The list to add to /// A concatenated list of the result lists of applying each function to each value /// - /// Note: this function has since been added to FSharp.Core. - /// It will be removed in next major release of FSharpPlus. + /// Same as List.Cons but with curried parameters. /// let cons value list = value :: list : list<'T> From 3619716c0db29728b1873fadd4972a513341ba30 Mon Sep 17 00:00:00 2001 From: gusty <1261319+gusty@users.noreply.github.com> Date: Thu, 29 May 2025 09:22:18 +0200 Subject: [PATCH 2/3] + cons and uncons --- src/FSharpPlus/Data/NonEmptyList.fs | 28 ++++++++++++++++++++++++---- src/FSharpPlus/Extensions/Array.fs | 19 +++++++++++++++++++ src/FSharpPlus/Extensions/List.fs | 13 ++++++++++++- src/FSharpPlus/Extensions/Seq.fs | 6 ++++++ 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/FSharpPlus/Data/NonEmptyList.fs b/src/FSharpPlus/Data/NonEmptyList.fs index 20591524d..96c652965 100644 --- a/src/FSharpPlus/Data/NonEmptyList.fs +++ b/src/FSharpPlus/Data/NonEmptyList.fs @@ -148,8 +148,25 @@ module NonEmptyList = let zipShortest (list1: NonEmptyList<'T>) (list2: NonEmptyList<'U>) = { Head = (list1.Head, list2.Head); Tail = List.zipShortest list1.Tail list2.Tail } - /// Returns a new NonEmptyList with the element added to the beginning. - let cons e {Head = x; Tail = xs} = {Head = e ; Tail = x::xs} + /// Adds an element to the beginning of the given list + /// The element to add + /// The list to add to + /// A new list with the element added to the beginning. + let cons e { Head = x; Tail = xs } = { Head = e ; Tail = x::xs } + + /// Splits the list in head and tail. + /// The input list. + /// A tuple with the head and the tail of the original list. + /// Thrown when the input list tail is empty. + let uncons ({ Head = x; Tail = xs } as list) = + match xs with + | [] -> invalidArg (nameof(list)) "The input sequence has an empty tail" + | _ -> x, ofList xs + + /// Splits the list in head and tail. + /// The input list. + /// A tuple with the head and the tail of the original list. + let unconsAsList ({ Head = x; Tail = xs } as list) = x, xs /// Returns the first element of a new non empty list. You can also use property nel.Head. let head {Head = x; Tail = _ } = x @@ -157,10 +174,13 @@ module NonEmptyList = /// Returns a new NonEmptyList of the elements trailing the first element. /// Thrown when the tail is empty. /// Throws exception for empty tail - let tail {Head = _; Tail = xs } = ofList xs + let tail ({ Head = _; Tail = xs } as list) = + match xs with + | [] -> invalidArg (nameof(list)) "The input sequence has an empty tail" + | _ -> ofList xs /// Returns a new NonEmptyList of the elements trailing the first element or None. - let tryTail {Head = _; Tail = xs } = tryOfList xs + let tryTail { Head = _; Tail = xs } = tryOfList xs let rec tails s = let {Tail = xs} = s match xs with diff --git a/src/FSharpPlus/Extensions/Array.fs b/src/FSharpPlus/Extensions/Array.fs index 5fea94a70..2639d8115 100644 --- a/src/FSharpPlus/Extensions/Array.fs +++ b/src/FSharpPlus/Extensions/Array.fs @@ -1,5 +1,7 @@ namespace FSharpPlus +#nowarn "1204" // Suppress warning about using FSharp Compiler's error strings. + /// Additional operations on Array [] module Array = @@ -8,6 +10,23 @@ module Array = open FSharp.Core.CompilerServices open FSharpPlus.Internals.Errors + /// Adds an element to the beginning of the given array + /// The element to add + /// The array to add to + /// A new array with the element added to the beginning. + let cons value array = + raiseIfNull (nameof(array)) array + Array.insertAt 0 value array + + /// Splits the array in head and tail. + /// The input array. + /// A tuple with the head and the tail of the original array. + /// Thrown when the input array is empty. + let uncons array = + raiseIfNull (nameof(array)) array + if Array.isEmpty array then invalidArg (nameof(array)) LanguagePrimitives.ErrorStrings.InputSequenceEmptyString + else array[0], array[1..] + /// Applies an array of functions to an array of values and concatenates them. /// The array of functions. /// The array of values. diff --git a/src/FSharpPlus/Extensions/List.fs b/src/FSharpPlus/Extensions/List.fs index 98549879a..e8340743f 100644 --- a/src/FSharpPlus/Extensions/List.fs +++ b/src/FSharpPlus/Extensions/List.fs @@ -1,5 +1,7 @@ namespace FSharpPlus +#nowarn "1204" // Suppress warning about using FSharp Compiler's error strings. + /// Additional operations on List [] module List = @@ -28,12 +30,21 @@ module List = /// Adds an element to the beginning of the given list /// The element to add /// The list to add to - /// A concatenated list of the result lists of applying each function to each value + /// A new list with the element added to the beginning. /// /// Same as List.Cons but with curried parameters. /// let cons value list = value :: list : list<'T> + /// Splits the list in head and tail. + /// The input list. + /// A tuple with the head and the tail of the original list. + /// Thrown when the input list is empty. + let uncons list = + match list with + | [] -> invalidArg (nameof(list)) LanguagePrimitives.ErrorStrings.InputSequenceEmptyString + | x::xs -> x, xs + /// Applies a list of functions to a list of values and concatenates them /// The list of functions. /// The list of values. diff --git a/src/FSharpPlus/Extensions/Seq.fs b/src/FSharpPlus/Extensions/Seq.fs index 438b1ec40..c493f5824 100644 --- a/src/FSharpPlus/Extensions/Seq.fs +++ b/src/FSharpPlus/Extensions/Seq.fs @@ -5,6 +5,12 @@ namespace FSharpPlus module Seq = open System + /// Adds an element to the beginning of the given sequence + /// The element to add + /// The sequence to add to + /// A new sequence with the element added to the beginning. + let cons value source = seq { yield value; yield! source } : seq<'T> + /// Applies the given function to each element of the sequence and concatenates the results. /// /// Remember sequence is lazy, effects are delayed until it is enumerated. From 156fd220c70dfe78d808a44dcdf4260217061a73 Mon Sep 17 00:00:00 2001 From: gusty <1261319+gusty@users.noreply.github.com> Date: Thu, 29 May 2025 09:31:50 +0200 Subject: [PATCH 3/3] Reuse cons --- src/FSharpPlus/Control/Traversable.fs | 22 ++++++++-------------- src/FSharpPlus/Lens.fs | 2 +- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/FSharpPlus/Control/Traversable.fs b/src/FSharpPlus/Control/Traversable.fs index 2f9bdbfa6..570ebd082 100644 --- a/src/FSharpPlus/Control/Traversable.fs +++ b/src/FSharpPlus/Control/Traversable.fs @@ -50,16 +50,14 @@ type Traverse = #if TEST_TRACE Traces.add "Traverse seq" #endif - let cons x y = seq {yield x; yield! y} - let cons_f x ys = Map.Invoke (cons: 'a -> seq<_> -> seq<_>) (f x) <*> ys + let cons_f x ys = Map.Invoke (Seq.cons: 'a -> seq<_> -> seq<_>) (f x) <*> ys Seq.foldBack cons_f t (result Seq.empty) static member inline Traverse (t: _ NonEmptySeq, f, []_output: 'R, []_impl: Default3) = #if TEST_TRACE Traces.add "Traverse NonEmptySeq" #endif - let cons x y = seq {yield x; yield! y} - let cons_f x ys = Map.Invoke (cons: 'a -> seq<_> -> seq<_>) (f x) <*> ys + let cons_f x ys = Map.Invoke (Seq.cons: 'a -> seq<_> -> seq<_>) (f x) <*> ys Map.Invoke NonEmptySeq.ofSeq (Seq.foldBack cons_f t (result Seq.empty)) static member inline Traverse (t: seq<'T>, f: 'T -> '``Functor<'U>``, []_output: '``Functor>``, []_impl: Default2) = @@ -166,14 +164,13 @@ type Traverse = #if TEST_TRACE Traces.add "Traverse []" #endif - let cons x y = Array.append [|x|] y let rec loop acc = function | [||] -> acc | xxs -> let x, xs = Array.head xxs, Array.tail xxs let v = f x - loop (cons v acc) xs - let cons_f x xs = Map.Invoke cons xs <*> x + loop (Array.cons v acc) xs + let cons_f x xs = Map.Invoke Array.cons xs <*> x Array.fold cons_f (result [||]) (loop [||] t) static member inline Invoke (f: 'T -> '``Functor<'U>``) (t: '``Traversable<'T>``) : '``Functor<'Traversable<'U>>`` = @@ -185,8 +182,7 @@ type Traverse = type Sequence with static member inline Sequence (t: _ seq, []_output: 'R, []_impl: Default5) : 'R = - let cons x y = seq { yield x; yield! y } - let cons_f x ys = Map.Invoke (cons: 'a -> seq<_> -> seq<_>) x <*> ys + let cons_f x ys = Map.Invoke (Seq.cons: 'a -> seq<_> -> seq<_>) x <*> ys Seq.foldBack cons_f t (result Seq.empty) static member inline Sequence (t: seq<'``Applicative<'T>``>, []_output: '``Applicative>`` , []_impl: Default4) : '``Applicative>`` = @@ -409,14 +405,13 @@ type Gather = #if TEST_TRACE Traces.add "Gather []" #endif - let cons x y = Array.append [|x|] y let rec loop acc = function | [||] -> acc | xxs -> let x, xs = Array.head xxs, Array.tail xxs let v = f x - loop (cons v acc) xs - let cons_f x xs = Map.Invoke cons xs <.> x + loop (Array.cons v acc) xs + let cons_f x xs = Map.Invoke Array.cons xs <.> x Array.fold cons_f (Pure.Invoke [||]) (loop [||] t) static member inline Invoke (f: 'T -> '``Functor<'U>``) (t: '``Traversable<'T>``) : '``Functor<'Traversable<'U>>`` = @@ -428,8 +423,7 @@ type Gather = type Transpose with static member inline Transpose (t: _ seq, []_output: 'R, []_impl: Default5) : 'R = - let cons x y = seq { yield x; yield! y } - let cons_f x ys = Map.Invoke (cons: 'a -> seq<_> -> seq<_>) x <.> ys + let cons_f x ys = Map.Invoke (Seq.cons: 'a -> seq<_> -> seq<_>) x <.> ys Seq.foldBack cons_f t (Pure.Invoke Seq.empty) static member inline Transpose (t: seq<'``Applicative<'T>``>, []_output: '``Applicative>`` , []_impl: Default4) : '``Applicative>`` = diff --git a/src/FSharpPlus/Lens.fs b/src/FSharpPlus/Lens.fs index c1a0ad37a..6421eaeea 100644 --- a/src/FSharpPlus/Lens.fs +++ b/src/FSharpPlus/Lens.fs @@ -186,7 +186,7 @@ module Lens = let foldlOf l f z = (flip Endo.run z << Dual.run) << foldMapOf l (Dual << Endo << flip f) /// Extract a list of the targets of a Fold. See also (^..). - let toListOf l = let cons x y = x :: y in foldrOf l cons [] + let toListOf l = foldrOf l List.cons [] /// Get the largest target of a Fold. let maximumOf l =