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 =