diff --git a/docsrc/tools/doclib.fs b/docsrc/tools/doclib.fs index 683fc289a..396cfad4a 100644 --- a/docsrc/tools/doclib.fs +++ b/docsrc/tools/doclib.fs @@ -4,7 +4,7 @@ module DocLib // Copyright 2010 Steffen Forkmann // Apache license - +#nowarn "49" open System.IO module String = diff --git a/src/FSharpPlus/Extensions/Array.fs b/src/FSharpPlus/Extensions/Array.fs index 2639d8115..511b5164d 100644 --- a/src/FSharpPlus/Extensions/Array.fs +++ b/src/FSharpPlus/Extensions/Array.fs @@ -14,17 +14,17 @@ module 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 + let cons value (array: 'T []) = + let array = nullArgCheck (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 + let uncons (array: 'T []) = + let array = nullArgCheck (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. @@ -38,42 +38,48 @@ module Array = /// val it : int [] = [|2; 4; 6; 3; 6; 9|] /// /// - let apply f x = - raiseIfNull (nameof(x)) x + let apply (f: ('T -> 'U) []) (x: 'T []) : 'U [] = + let x = nullArgCheck (nameof x) x let lenf, lenx = Array.length f, Array.length x Array.init (lenf * lenx) (fun i -> let (d, r) = Math.DivRem (i, lenx) in f.[d] x.[r]) - /// Combines all values from the first array with the second, using the supplied mapping function. - let lift2 f x y = - raiseIfNull (nameof(x)) x - raiseIfNull (nameof(y)) y + /// Combines all values from three arrays and calls a mapping function on this combination. + /// Mapping function taking three element combination as input. + /// First array. + /// Second array. + /// + /// Array with values returned from mapping function. + let lift2 (mapping: 'T1 -> 'T2 -> 'U) (array1: 'T1 []) (array2: 'T2 []) : 'U [] = + let array1 = nullArgCheck (nameof array1) array1 + let array2 = nullArgCheck (nameof array2) array2 - let lenx, leny = Array.length x, Array.length y - Array.init (lenx * leny) (fun i -> let (d, r) = Math.DivRem (i, leny) in f x.[d] y.[r]) + let lenx, leny = Array.length array1, Array.length array2 + Array.init (lenx * leny) (fun i -> let (d, r) = Math.DivRem (i, leny) in mapping array1.[d] array2.[r]) /// Combines all values from three arrays and calls a mapping function on this combination. /// Mapping function taking three element combination as input. - /// First array. - /// Second array. - /// Third array. + /// First array. + /// Second array. + /// Third array. /// /// Array with values returned from mapping function. - let lift3 mapping list1 list2 list3 = - raiseIfNull (nameof(list1)) list1 - raiseIfNull (nameof(list2)) list2 - raiseIfNull (nameof(list3)) list3 + let lift3 (mapping: 'T1 -> 'T2 -> 'T3 -> 'U) (array1: 'T1 []) (array2: 'T2 []) (array3: 'T3 []) : 'U [] = + let array1 = nullArgCheck (nameof array1) array1 + let array2 = nullArgCheck (nameof array2) array2 + let array3 = nullArgCheck (nameof array3) array3 - let lenx, leny, lenz = Array.length list1, Array.length list2, Array.length list3 - let combinedFirstTwo = Array.init (lenx * leny) (fun i -> let (d, r) = Math.DivRem (i, leny) in (list1.[d], list2.[r])) + let lenx, leny, lenz = Array.length array1, Array.length array2, Array.length array3 + let combinedFirstTwo = Array.init (lenx * leny) (fun i -> let (d, r) = Math.DivRem (i, leny) in (array1.[d], array2.[r])) - Array.init (lenx * leny * lenz) (fun i -> let (d, r) = Math.DivRem (i, lenz) in combinedFirstTwo.[d], list3.[r]) + Array.init (lenx * leny * lenz) (fun i -> let (d, r) = Math.DivRem (i, lenz) in combinedFirstTwo.[d], array3.[r]) |> Array.map (fun x -> mapping (fst (fst x)) (snd (fst x)) (snd x)) /// Concatenates all elements, using the specified separator between each element. - let intercalate (separator: 'T []) (source: seq<'T []>) = - raiseIfNull (nameof(source)) source + let intercalate (separator: 'T []) (source: seq<'T []>) : 'T [] = + let separator = nullArgCheck (nameof separator) separator + let source = nullArgCheck (nameof source) source #if FABLE_COMPILER source |> Seq.intercalate separator |> Seq.toArray @@ -88,8 +94,8 @@ module Array = #endif /// Inserts a separator element between each element in the source array. - let intersperse element (source: 'T []) = - raiseIfNull (nameof(source)) source + let intersperse element (source: 'T []) : 'T [] = + let source = nullArgCheck (nameof source) source match source with | [||] -> [||] @@ -101,17 +107,17 @@ module Array = | _ -> element) /// Creates a sequence of arrays by splitting the source array on any of the given separators. - let split (separators: seq<_ []>) (source: _ []) = - raiseIfNull (nameof(separators)) separators - raiseIfNull (nameof(source)) source + let split (separators: seq<'T []>) (source: 'T []) : seq<'T []> = + let separators = nullArgCheck (nameof separators) separators + let source = nullArgCheck (nameof source) source source |> Array.toSeq |> Seq.split separators |> Seq.map Seq.toArray /// Replaces a subsequence of the source array with the given replacement array. let replace (oldValue: 'T []) (newValue: 'T []) (source: 'T[]) : 'T[] = - raiseIfNull (nameof(oldValue)) oldValue - raiseIfNull (nameof(newValue)) newValue - raiseIfNull (nameof(source)) source + let oldValue = nullArgCheck (nameof oldValue) oldValue + let newValue = nullArgCheck (nameof newValue) newValue + let source = nullArgCheck (nameof source) source #if FABLE_COMPILER source |> Array.toSeq |> Seq.replace oldValue newValue |> Seq.toArray: 'T [] @@ -178,9 +184,9 @@ module Array = /// /// The index of the slice or None. /// - let findSliceIndex (slice: _ []) (source: _ []) = - raiseIfNull (nameof(slice)) slice - raiseIfNull (nameof(source)) source + let findSliceIndex (slice: 'T []) (source: 'T []) : int = + let slice = nullArgCheck (nameof slice) slice + let source = nullArgCheck (nameof source) source let index = Internals.FindSliceIndex.arrayImpl slice source if index = -1 then @@ -195,9 +201,9 @@ module Array = /// /// The index of the slice or None. /// - let tryFindSliceIndex (slice: _ []) (source: _ []) = - raiseIfNull (nameof(slice)) slice - raiseIfNull (nameof(source)) source + let tryFindSliceIndex (slice: 'T []) (source: 'T []) : int option = + let slice = nullArgCheck (nameof slice) slice + let source = nullArgCheck (nameof source) source let index = Internals.FindSliceIndex.arrayImpl slice source if index = -1 then None else Some index @@ -209,9 +215,9 @@ module Array = /// /// The index of the slice or None. /// - let findLastSliceIndex (slice: _ []) (source: _ []) = - raiseIfNull (nameof(slice)) slice - raiseIfNull (nameof(source)) source + let findLastSliceIndex (slice: _ []) (source: _ []) : int = + let slice = nullArgCheck (nameof slice) slice + let source = nullArgCheck (nameof source) source let index = Internals.FindLastSliceIndex.arrayImpl slice source if index = -1 then @@ -226,9 +232,9 @@ module Array = /// /// The index of the slice or None. /// - let tryFindLastSliceIndex (slice: _ []) (source: _ []) = - raiseIfNull (nameof(slice)) slice - raiseIfNull (nameof(source)) source + let tryFindLastSliceIndex (slice: 'T []) (source: 'T []) : int option = + let slice = nullArgCheck (nameof slice) slice + let source = nullArgCheck (nameof source) source let index = Internals.FindLastSliceIndex.arrayImpl slice source if index = -1 then None else Some index @@ -241,8 +247,8 @@ module Array = /// /// A tuple with both resulting arrays. /// - let partitionMap (mapper: 'T -> Choice<'T1,'T2>) (source: array<'T>) = - raiseIfNull (nameof(source)) source + let partitionMap (mapper: 'T -> Choice<'T1, 'T2>) (source: array<'T>) : array<'T1> * array<'T2> = + let source = nullArgCheck (nameof source) source let (x, y) = ResizeArray (), ResizeArray () Array.iter (mapper >> function Choice1Of2 e -> x.Add e | Choice2Of2 e -> y.Add e) source @@ -251,19 +257,19 @@ module Array = /// Safely build a new array whose elements are the results of applying the given function /// to each of the elements of the two arrays pairwise. /// If one array is shorter, excess elements are discarded from the right end of the longer array. - let map2Shortest f (a1: 'T []) (a2: 'U []) = - raiseIfNull (nameof(a1)) a1 - raiseIfNull (nameof(a2)) a2 + let map2Shortest (f: 'T1 -> 'T2 -> 'U) (a1: 'T1 []) (a2: 'T2 []) : 'U [] = + let a1 = nullArgCheck (nameof a1) a1 + let a2 = nullArgCheck (nameof a2) a2 Array.init (min a1.Length a2.Length) (fun i -> f a1.[i] a2.[i]) /// Safely build a new array whose elements are the results of applying the given function /// to each of the elements of the three arrays pairwise. /// If one array is shorter, excess elements are discarded from the right end of the longer array. - let map3Shortest f (a1: 'T1 []) (a2: 'T2 []) (a3: 'T3 []) = - raiseIfNull (nameof a1) a1 - raiseIfNull (nameof a2) a2 - raiseIfNull (nameof a3) a3 + let map3Shortest (f: 'T1 -> 'T2 -> 'T3 -> 'U) (a1: 'T1 []) (a2: 'T2 []) (a3: 'T3 []) : 'U [] = + let a1 = nullArgCheck (nameof a1) a1 + let a2 = nullArgCheck (nameof a2) a2 + let a3 = nullArgCheck (nameof a3) a3 Array.init (min a1.Length a2.Length |> min a3.Length) (fun i -> f a1.[i] a2.[i] a3.[i]) /// @@ -272,9 +278,9 @@ module Array = /// First input array. /// Second input array. /// Array with corresponding pairs of input arrays. - let zipShortest (a1: array<'T1>) (a2: array<'T2>) = - raiseIfNull (nameof(a1)) a1 - raiseIfNull (nameof(a2)) a2 + let zipShortest (a1: array<'T1>) (a2: array<'T2>) : array<'T1 * 'T2> = + let a1 = nullArgCheck (nameof a1) a1 + let a2 = nullArgCheck (nameof a2) a2 Array.init (min a1.Length a2.Length) (fun i -> a1.[i], a2.[i]) @@ -285,10 +291,10 @@ module Array = /// Second input array. /// Third input array. /// Array with corresponding tuple of input arrays. - let zip3Shortest (a1: array<'T1>) (a2: array<'T2>) (a3: array<'T3>) = - raiseIfNull (nameof a1) a1 - raiseIfNull (nameof a2) a2 - raiseIfNull (nameof a3) a3 + let zip3Shortest (a1: array<'T1>) (a2: array<'T2>) (a3: array<'T3>) : array<'T1 * 'T2 * 'T3> = + let a1 = nullArgCheck (nameof a1) a1 + let a2 = nullArgCheck (nameof a2) a2 + let a3 = nullArgCheck (nameof a3) a3 Array.init (min a1.Length a2.Length |> min a3.Length) (fun i -> a1.[i], a2.[i], a3.[i]) /// Same as choose but with access to the index. @@ -296,8 +302,8 @@ module Array = /// The input array. /// /// Array with values x for each Array value where the function returns Some(x). - let choosei mapping source = - raiseIfNull (nameof(source)) source + let choosei (mapping: int -> 'T -> 'U option) (source: array<'T>) : array<'U> = + let source = nullArgCheck (nameof source) source let mutable i = ref -1 let fi x = diff --git a/src/FSharpPlus/Extensions/Enumerator.fs b/src/FSharpPlus/Extensions/Enumerator.fs index 6fd3c50bb..a86fc3141 100644 --- a/src/FSharpPlus/Extensions/Enumerator.fs +++ b/src/FSharpPlus/Extensions/Enumerator.fs @@ -1,5 +1,6 @@ namespace FSharpPlus +#nowarn "3261" // nullness checks #if !FABLE_COMPILER /// Additional operations on IEnumerator diff --git a/src/FSharpPlus/Extensions/Exception.fs b/src/FSharpPlus/Extensions/Exception.fs index 08b8e6030..2ff821e95 100644 --- a/src/FSharpPlus/Extensions/Exception.fs +++ b/src/FSharpPlus/Extensions/Exception.fs @@ -11,15 +11,15 @@ module Exception = /// Throws the given exception with its original stacktrace. let inline rethrow<'T> (exn: exn) = - raiseIfNull (nameof exn) exn + let exn = nullArgCheck (nameof exn) exn (ExceptionDispatchInfo.Capture exn).Throw () Unchecked.defaultof<'T> /// Combines exceptions from 2 exceptions into a single AggregateException. /// Exceptions already present in the first argument won't be added. let add (exn1: exn) (exn2: exn) = - raiseIfNull (nameof exn1) exn1 - raiseIfNull (nameof exn2) exn2 + let exn1 = nullArgCheck (nameof exn1) exn1 + let exn2 = nullArgCheck (nameof exn2) exn2 let f (e: exn) = match e with :? AggregateException as a -> a.InnerExceptions :> seq<_> diff --git a/src/FSharpPlus/Extensions/Extensions.fs b/src/FSharpPlus/Extensions/Extensions.fs index c9e42f3ec..83409e559 100644 --- a/src/FSharpPlus/Extensions/Extensions.fs +++ b/src/FSharpPlus/Extensions/Extensions.fs @@ -4,6 +4,7 @@ namespace FSharpPlus module Extensions = open System + open FSharpPlus.Internals.Errors type Collections.Generic.IEnumerable<'T> with member this.GetSlice = function @@ -39,7 +40,7 @@ module Extensions = let private (|Canceled|Faulted|Completed|) (t: Task<'a>) = if t.IsCanceled then Canceled - else if t.IsFaulted then Faulted t.Exception + else if t.IsFaulted then Faulted (Unchecked.nonNull t.Exception) else Completed t.Result type Task<'t> with @@ -158,7 +159,7 @@ module Extensions = Async.FromContinuations (fun (sc, ec, cc) -> task.ContinueWith (fun (task: Task<'T>) -> if task.IsFaulted then - let e = task.Exception + let e = Unchecked.nonNull task.Exception if e.InnerExceptions.Count = 1 then ec e.InnerExceptions[0] else ec e elif task.IsCanceled then cc (TaskCanceledException ()) @@ -185,7 +186,7 @@ module Extensions = Async.FromContinuations (fun (sc, ec, cc) -> task.ContinueWith (fun (task: Task) -> if task.IsFaulted then - let e = task.Exception + let e = Unchecked.nonNull task.Exception if e.InnerExceptions.Count = 1 then ec e.InnerExceptions[0] else ec e elif task.IsCanceled then cc (TaskCanceledException ()) diff --git a/src/FSharpPlus/Extensions/HashSet.fs b/src/FSharpPlus/Extensions/HashSet.fs index c0db14b3c..0e392f5ce 100644 --- a/src/FSharpPlus/Extensions/HashSet.fs +++ b/src/FSharpPlus/Extensions/HashSet.fs @@ -31,8 +31,8 @@ module HashSet = /// The union of set1 and set2. [] let union (source1: HashSet<'T>) (source2: HashSet<'T>) : HashSet<'T> = - raiseIfNull (nameof source1) source1 - raiseIfNull (nameof source2) source2 + let source1 = nullArgCheck (nameof source1) source1 + let source2 = nullArgCheck (nameof source2) source2 let union = #if FABLE_COMPILER HashSet<'T> () @@ -50,7 +50,7 @@ module HashSet = /// A set containing the transformed elements. [] let map (mapping: 'T -> 'U) (source: HashSet<'T>) : HashSet<'U> = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source let result = empty<'U> for item in source do result.Add (mapping item) |> ignore @@ -62,7 +62,7 @@ module HashSet = /// true if the set contains value; otherwise, false. [] let contains (value: 'T) (source: HashSet<'T>) : bool = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source.Contains value /// Determines whether the first set is a subset of the second set. @@ -71,6 +71,6 @@ module HashSet = /// true if source1 is a subset of source2; otherwise, false. [] let isSubset (source1: HashSet<'T>) (source2: HashSet<'T>) : bool = - raiseIfNull (nameof source1) source1 - raiseIfNull (nameof source2) source2 + let source1 = nullArgCheck (nameof source1) source1 + let source2 = nullArgCheck (nameof source2) source2 source1.IsSubsetOf source2 diff --git a/src/FSharpPlus/Extensions/List.fs b/src/FSharpPlus/Extensions/List.fs index e8340743f..4c9c32b16 100644 --- a/src/FSharpPlus/Extensions/List.fs +++ b/src/FSharpPlus/Extensions/List.fs @@ -8,6 +8,7 @@ module List = open System open FSharp.Core.CompilerServices + open FSharpPlus.Internals.Errors /// Returns a list that contains one item only. /// @@ -475,7 +476,7 @@ module List = open System.Reflection /// Creates an infinite list which cycles the element of the source. - let cycle lst = + let cycle (lst: 'T list) = let last = ref lst let rec copy = function | [] -> failwith "empty list" @@ -486,7 +487,7 @@ module List = | x::xs -> x::copy xs let cycled = copy lst let strs = last.Value.GetType().GetFields(BindingFlags.NonPublic ||| BindingFlags.Instance) |> Array.map (fun field -> field.Name) - let tailField = last.Value.GetType().GetField(Array.find(fun (s:string) -> s.ToLower().Contains("tail")) strs, BindingFlags.NonPublic ||| BindingFlags.Instance) + let tailField = last.Value.GetType().GetField(Array.find(fun (s:string) -> s.ToLower().Contains("tail")) strs, BindingFlags.NonPublic ||| BindingFlags.Instance) |> Unchecked.nonNull tailField.SetValue(last.Value, cycled) cycled #else diff --git a/src/FSharpPlus/Extensions/ResizeArray.fs b/src/FSharpPlus/Extensions/ResizeArray.fs index 7a2e02f41..bc2deb148 100644 --- a/src/FSharpPlus/Extensions/ResizeArray.fs +++ b/src/FSharpPlus/Extensions/ResizeArray.fs @@ -4,7 +4,6 @@ [] module ResizeArray = - open System open FSharpPlus.Internals.Errors /// Builds a new ResizeArray whose elements are the results of applying the given function @@ -16,8 +15,8 @@ module ResizeArray = /// The result ResizeArray. /// /// Thrown when the input ResizeArray is null. - let map (mapping: 'T->'U) (source: ResizeArray<'T>) = - raiseIfNull (nameof source) source + let map (mapping: 'T -> 'U) (source: ResizeArray<'T>) = + let source = nullArgCheck (nameof source) source ResizeArray (Seq.map mapping source) @@ -25,13 +24,13 @@ module ResizeArray = /// The function to apply to elements from the input ResizeArray. /// The input ResizeArray. let iter (action: 'T -> unit) (source: ResizeArray<'T>) : unit = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source Seq.iter action source /// Applies a ResizeArray of functions to a ResizeArray of values and concatenates them. /// The functions. - /// The values. + /// The values. /// A concatenated list of the resulting ResizeArray after applying each function to each value. /// /// @@ -40,16 +39,16 @@ module ResizeArray = /// val it : int list = [2; 4; 6; 3; 6; 9] /// /// - let apply (f: ResizeArray<'T->'U>) (ra: ResizeArray<'T>) = - raiseIfNull (nameof ra) ra + let apply (f: ResizeArray<'T -> 'U>) (source: ResizeArray<'T>) : ResizeArray<'U> = + let source = nullArgCheck (nameof source) source - ResizeArray (Seq.apply f ra) + ResizeArray (Seq.apply f source) /// Combines all values from the first ResizeArray with the second, using the supplied mapping function. - let lift2 mapping (ra1: ResizeArray<'T>) (ra2: ResizeArray<'U>) = - raiseIfNull (nameof ra1) ra1 - raiseIfNull (nameof ra2) ra2 - + let lift2 mapping (ra1: ResizeArray<'T1>) (ra2: ResizeArray<'T2>) : ResizeArray<'U> = + let ra1 = nullArgCheck (nameof ra1) ra1 + let ra2 = nullArgCheck (nameof ra2) ra2 + ResizeArray (Seq.lift2 mapping ra1 ra2) /// Combines values from three ResizeArrays and calls a mapping function on this combination. @@ -59,37 +58,38 @@ module ResizeArray = /// Third ResizeArray. /// /// ResizeArray with values returned from mapping function. - let lift3 mapping (ra1: ResizeArray<'T>) (ra2: ResizeArray<'U>) (ra3: ResizeArray<'V>) = - raiseIfNull (nameof ra1) ra1 - raiseIfNull (nameof ra2) ra2 - raiseIfNull (nameof ra3) ra3 - + let lift3 mapping (ra1: ResizeArray<'T1>) (ra2: ResizeArray<'T2>) (ra3: ResizeArray<'T3>) : ResizeArray<'U> = + let ra1 = nullArgCheck (nameof ra1) ra1 + let ra2 = nullArgCheck (nameof ra2) ra2 + let ra3 = nullArgCheck (nameof ra3) ra3 + ResizeArray (Seq.lift3 mapping ra1 ra2 ra3) /// Concatenates all elements, using the specified separator between each element. - let intercalate (separator: _ []) (source: seq<_ []>) = - raiseIfNull (nameof separator) separator - raiseIfNull (nameof source) source + //let intercalate (separator) (source) = + let intercalate (separator: ResizeArray<'T>) (source: seq>) : ResizeArray<'T> = + let separator = nullArgCheck (nameof separator) separator + let source = nullArgCheck (nameof source) source - source |> Seq.intercalate separator |> Seq.toArray + source |> Seq.intercalate separator |> ResizeArray /// Inserts a separator element between each element in the source ResizeArray. - let intersperse element source = - raiseIfNull (nameof element) element - raiseIfNull (nameof source) source - - source |> Array.toSeq |> Seq.intersperse element |> Seq.toArray : 'T [] + let intersperse (element: 'T) (source: ResizeArray<'T>) : ResizeArray<'T> = + source |> nullArgCheck (nameof source) |> Seq.intersperse element |> ResizeArray /// Creates a sequence of arrays by splitting the source array on any of the given separators. let split (separators: seq>) (source: ResizeArray<'T>) : seq> = - raiseIfNull (nameof separators) separators - raiseIfNull (nameof source) source + let separators = nullArgCheck (nameof separators) separators + let source = nullArgCheck (nameof source) source + source |> Seq.split separators |> Seq.map ResizeArray /// Replaces a subsequence of the source array with the given replacement array. let replace (oldValue: ResizeArray<'T>) (newValue: ResizeArray<'T>) (source: ResizeArray<'T>) : ResizeArray<'T> = - raiseIfNull (nameof oldValue) oldValue - raiseIfNull (nameof source) source + let oldValue = nullArgCheck (nameof oldValue) oldValue + let newValue = nullArgCheck (nameof newValue) newValue + let source = nullArgCheck (nameof source) source + source |> Seq.replace oldValue newValue |> ResizeArray #if !FABLE_COMPILER @@ -104,8 +104,8 @@ module ResizeArray = /// The index of the slice. /// let findSliceIndex (slice: ResizeArray<'T>) (source: ResizeArray<'T>) : int = - raiseIfNull (nameof slice) slice - raiseIfNull (nameof source) source + let slice = nullArgCheck (nameof slice) slice + let source = nullArgCheck (nameof source) source let index = Internals.FindSliceIndex.seqImpl slice source if index = -1 then @@ -121,8 +121,8 @@ module ResizeArray = /// The index of the slice or None. /// let tryFindSliceIndex (slice: ResizeArray<'T>) (source: ResizeArray<'T>) : int option = - raiseIfNull (nameof slice) slice - raiseIfNull (nameof source) source + let slice = nullArgCheck (nameof slice) slice + let source = nullArgCheck (nameof source) source let index = Internals.FindSliceIndex.seqImpl slice source if index = -1 then None else Some index @@ -137,6 +137,9 @@ module ResizeArray = /// The index of the slice. /// let findLastSliceIndex (slice: ResizeArray<'T>) (source: ResizeArray<'T>) : int = + let slice = nullArgCheck (nameof slice) slice + let source = nullArgCheck (nameof source) source + let index = Internals.FindLastSliceIndex.seqImpl slice source if index = -1 then invalidArg (nameof slice) "The specified slice was not found in the sequence." @@ -151,6 +154,9 @@ module ResizeArray = /// The index of the slice or None. /// let tryFindLastSliceIndex (slice: ResizeArray<'T>) (source: ResizeArray<'T>) : int option = + let slice = nullArgCheck (nameof slice) slice + let source = nullArgCheck (nameof source) source + let index = Internals.FindLastSliceIndex.seqImpl slice source if index = -1 then None else Some index #endif @@ -163,7 +169,7 @@ module ResizeArray = /// A tuple with both resulting arrays. /// let partitionMap (mapper: 'T -> Choice<'T1,'T2>) (source: ResizeArray<'T>) : ResizeArray<'T1> * ResizeArray<'T2> = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source let (x, y) = ResizeArray (), ResizeArray () iter (mapper >> function Choice1Of2 e -> x.Add e | Choice2Of2 e -> y.Add e) source @@ -172,12 +178,12 @@ module ResizeArray = /// Safely build a new ResizeArray whose elements are the results of applying the given function /// to each of the elements of the two ResizeArrays pairwise. /// If one array is shorter, excess elements are discarded from the right end of the longer array. - let map2Shortest f (a1: ResizeArray<_>) (a2: ResizeArray<_>) = - raiseIfNull (nameof a1) a1 - raiseIfNull (nameof a2) a2 - + let map2Shortest (f: 'T1 -> 'T2 -> 'U) (a1: ResizeArray<'T1>) (a2: ResizeArray<'T2>) : ResizeArray<'U> = + let a1 = nullArgCheck (nameof a1) a1 + let a2 = nullArgCheck (nameof a2) a2 + let len = min a1.Count a2.Count - let ra = ResizeArray(len) + let ra = ResizeArray len for i in 0..(len-1) do ra.Add (f a1[i] a2[i]) ra @@ -185,7 +191,11 @@ module ResizeArray = /// Safely build a new ResizeArray whose elements are the results of applying the given function /// to each of the elements of the three ResizeArrays pairwise. /// If one array is shorter, excess elements are discarded from the right end of the longer array. - let map3Shortest f (a1: ResizeArray<'T1>) (a2: ResizeArray<'T2>) (a3: ResizeArray<'T3>) = + let map3Shortest (f: 'T1 -> 'T2 -> 'T3 -> 'U) (a1: ResizeArray<'T1>) (a2: ResizeArray<'T2>) (a3: ResizeArray<'T3>) : ResizeArray<'U> = + let a1 = nullArgCheck (nameof a1) a1 + let a2 = nullArgCheck (nameof a2) a2 + let a3 = nullArgCheck (nameof a3) a3 + let len = min a1.Count a2.Count |> min a3.Count let ra = ResizeArray len for i in 0..(len-1) do @@ -198,10 +208,10 @@ module ResizeArray = /// First input ResizeArray. /// Second input ResizeArray. /// ResizeArray with corresponding pairs of input ResizeArrays. - let zipShortest (a1: ResizeArray<'T1>) (a2: ResizeArray<'T2>) = - raiseIfNull (nameof a1) a1 - raiseIfNull (nameof a2) a2 - + let zipShortest (a1: ResizeArray<'T1>) (a2: ResizeArray<'T2>) : ResizeArray<'T1 * 'T2> = + let a1 = nullArgCheck (nameof a1) a1 + let a2 = nullArgCheck (nameof a2) a2 + let len = min a1.Count a2.Count let ra = ResizeArray(len) for i in 0..(len-1) do @@ -215,7 +225,11 @@ module ResizeArray = /// Second input ResizeArray. /// Third input ResizeArray. /// ResizeArray with corresponding pairs of input ResizeArrays. - let zip3Shortest (a1: ResizeArray<'T1>) (a2: ResizeArray<'T2>) (a3: ResizeArray<'T3>) = + let zip3Shortest (a1: ResizeArray<'T1>) (a2: ResizeArray<'T2>) (a3: ResizeArray<'T3>) : ResizeArray<'T1 * 'T2 * 'T3> = + let a1 = nullArgCheck (nameof a1) a1 + let a2 = nullArgCheck (nameof a2) a2 + let a3 = nullArgCheck (nameof a3) a3 + let len = min a1.Count a2.Count |> min a3.Count let ra = ResizeArray len for i in 0..(len-1) do diff --git a/src/FSharpPlus/Extensions/Seq.fs b/src/FSharpPlus/Extensions/Seq.fs index 90f73e8cc..c7d841f05 100644 --- a/src/FSharpPlus/Extensions/Seq.fs +++ b/src/FSharpPlus/Extensions/Seq.fs @@ -4,12 +4,15 @@ namespace FSharpPlus [] module Seq = open System + open FSharpPlus.Internals.Errors /// 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> + let cons value (source: seq<'T>) : seq<'T> = + let source = nullArgCheck (nameof source) source + seq { yield value; yield! source } /// Applies the given function to each element of the sequence and concatenates the results. /// @@ -23,7 +26,10 @@ module Seq = /// The result sequence. /// /// Thrown when the input sequence is null. - let bind (mapping: 'T->seq<'U>) source = Seq.collect mapping source + let bind (mapping: 'T -> seq<'U>) (source: seq<'T>) : seq<'U> = + let source = nullArgCheck (nameof source) source + + Seq.collect mapping source /// Applies a sequence of functions to a sequence of values and concatenates them. /// The seq of functions. @@ -36,10 +42,16 @@ module Seq = /// val it : seq<int> = seq [2; 4; 6; 3; ...] /// /// - let apply f x = bind (fun f -> Seq.map ((<|) f) x) f + let apply (f: seq<'T -> 'U>) (x: seq<'T>) : seq<'U> = + let f = nullArgCheck (nameof f) f + let x = nullArgCheck (nameof x) x + bind (fun f -> Seq.map ((<|) f) x) f /// Combines all values from the first seq with the second, using the supplied mapping function. - let lift2 f x1 x2 = Seq.allPairs x1 x2 |> Seq.map (fun (x, y) -> f x y) + let lift2 (f: 'T1 -> 'T2 -> 'U) (x1: seq<'T1>) (x2: seq<'T2>) : seq<'U> = + let x1 = nullArgCheck (nameof x1) x1 + let x2 = nullArgCheck (nameof x2) x2 + Seq.allPairs x1 x2 |> Seq.map (fun (x, y) -> f x y) /// Combines values from three seq and calls a mapping function on this combination. @@ -50,6 +62,10 @@ module Seq = /// /// Seq with values returned from mapping function. let lift3 (f: 'T1 -> 'T2 -> 'T3 -> 'U) (x1: seq<'T1>) (x2: seq<'T2>) (x3: seq<'T3>) : seq<'U> = + let x1 = nullArgCheck (nameof x1) x1 + let x2 = nullArgCheck (nameof x2) x2 + let x3 = nullArgCheck (nameof x3) x3 + Seq.allPairs x2 x3 |> Seq.allPairs x1 |> Seq.map (fun x -> (fst x, fst (snd x), snd (snd x))) @@ -66,7 +82,10 @@ module Seq = /// Note: this function has since been added to FSharp.Core. /// It will be removed in next major release of FSharpPlus. /// - let foldBack folder source state = Array.foldBack folder (Seq.toArray source) state + let foldBack folder (source: seq<'T>) (state: 'State) : 'State = + let source = nullArgCheck (nameof source) source + + Array.foldBack folder (Seq.toArray source) state /// /// Chunks the seq up into groups with the same projected key by applying @@ -86,41 +105,54 @@ module Seq = /// The input seq. /// /// The resulting sequence of keys tupled with an array of matching values - let chunkBy (projection: 'T -> 'Key) (source: _ seq) = seq { - use e = source.GetEnumerator () - if e.MoveNext () then - let mutable g = projection e.Current - let mutable members = ResizeArray () - members.Add e.Current - while e.MoveNext () do - let key = projection e.Current - if g = key then members.Add e.Current - else - yield g, members - g <- key - members <- ResizeArray () - members.Add e.Current - yield g, members } + let chunkBy (projection: 'T -> 'Key) (source: seq<'T>) : seq<'Key * ResizeArray<'T>> = + let source = nullArgCheck (nameof source) source + + seq { + use e = source.GetEnumerator () + if e.MoveNext () then + let mutable g = projection e.Current + let mutable members = ResizeArray () + members.Add e.Current + while e.MoveNext () do + let key = projection e.Current + if g = key then members.Add e.Current + else + yield g, members + g <- key + members <- ResizeArray () + members.Add e.Current + yield g, members } /// Inserts a separator element between each element in the source seq. - ///http://codebetter.com/matthewpodwysocki/2009/05/06/functionally-implementing-intersperse/ - let intersperse sep list = seq { - let mutable notFirst = false - for element in list do - if notFirst then yield sep - yield element - notFirst <- true } + ///http://codebetter.com/matthewpodwysocki/2009/05/06/functionally-implementing-intersperse/ + let intersperse sep (list: seq<'T>) : seq<'T> = + let list = nullArgCheck (nameof list) list + + seq { + let mutable notFirst = false + for element in list do + if notFirst then yield sep + yield element + notFirst <- true } /// Inserts a separator between each element in the source sequence. - let intercalate separator source = seq { - let mutable notFirst = false - for element in source do - if notFirst then yield! separator - yield! element - notFirst <- true } + let intercalate (separator: seq<'T>) (source: seq<#seq<'T>>) : seq<'T> = + let separator = nullArgCheck (nameof separator) separator + let source = nullArgCheck (nameof source) source + + seq { + let mutable notFirst = false + for element in source do + if notFirst then yield! separator + yield! element + notFirst <- true } /// Creates a sequence of sequences by splitting the source sequence on any of the given separators. - let split separators source = + let split (separators: seq<#seq<'T>>) (source: seq<'T>) : seq> = + let separators = nullArgCheck (nameof separators) separators + let source = nullArgCheck (nameof source) source + let split options = seq { match separators |> Seq.map Seq.toList |> Seq.toList with | [] -> yield source @@ -147,26 +179,31 @@ module Seq = split StringSplitOptions.None /// Replaces a subsequence of the source seq with the given replacement seq. - let replace (oldValue: seq<'T>) (newValue: seq<'T>) (source: seq<'T>) : seq<'T> = seq { - let old = oldValue |> Seq.toList - if old.Length = 0 then - yield! source - else - let candidate = ResizeArray old.Length - let mutable sindex = 0 - for item in source do - candidate.Add item - if item = old.[sindex] then - sindex <- sindex + 1 - if sindex >= old.Length then + let replace (oldValue: seq<'T>) (newValue: seq<'T>) (source: seq<'T>) : seq<'T> = + let oldValue = nullArgCheck (nameof oldValue) oldValue + let newValue = nullArgCheck (nameof newValue) newValue + let source = nullArgCheck (nameof source) source + + seq { + let old = Seq.toList oldValue + if old.Length = 0 then + yield! source + else + let candidate = ResizeArray old.Length + let mutable sindex = 0 + for item in source do + candidate.Add item + if item = old.[sindex] then + sindex <- sindex + 1 + if sindex >= old.Length then + sindex <- 0 + yield! newValue + candidate.Clear () + else sindex <- 0 - yield! newValue + yield! candidate candidate.Clear () - else - sindex <- 0 - yield! candidate - candidate.Clear () - yield! candidate } + yield! candidate } /// Returns a sequence that drops N elements of the original sequence and then yields the /// remaining elements of the sequence. @@ -176,7 +213,9 @@ module Seq = /// The input sequence. /// /// The result sequence. - let drop count (source: seq<_>) = + let drop count (source: seq<_>) : seq<_> = + let source = nullArgCheck (nameof source) source + seq { let mutable i = count @@ -196,7 +235,7 @@ module Seq = /// Note: this function has since been added to FSharp.Core. /// It will be removed in next major release of FSharpPlus. /// - let replicate count initial = Linq.Enumerable.Repeat (initial, count) + let replicate count (initial: 'T) : seq<'T> = Linq.Enumerable.Repeat (initial, count) #endif open System.Collections.ObjectModel @@ -207,7 +246,8 @@ module Seq = /// Converts a seq to an IReadOnlyList (from System.Collections.Generic). /// The seq source /// The seq converted to a System.Collections.Generic.IReadOnlyList - let toIReadOnlyList (source: seq<_>) = source |> ResizeArray |> ReadOnlyCollection :> IReadOnlyList<_> + let toIReadOnlyList (source: seq<'T>) : IReadOnlyList<'T> = + source |> nullArgCheck (nameof source) |> ResizeArray |> ReadOnlyCollection :> IReadOnlyList<_> #endif #if !FABLE_COMPILER /// @@ -224,7 +264,10 @@ module Seq = /// /// The index of the slice. /// - let findSliceIndex (slice: seq<_>) (source: seq<_>) = + let findSliceIndex (slice: seq<'T>) (source: seq<'T>) : int = + let slice = nullArgCheck (nameof slice) slice + let source = nullArgCheck (nameof source) source + #if !FABLE_COMPILER let index = Internals.FindSliceIndex.seqImpl slice source #else @@ -247,7 +290,10 @@ module Seq = /// /// The index of the slice or None. /// - let tryFindSliceIndex (slice: seq<_>) (source: seq<_>) = + let tryFindSliceIndex (slice: seq<'T>) (source: seq<'T>) : int option = + let slice = nullArgCheck (nameof slice) slice + let source = nullArgCheck (nameof source) source + #if !FABLE_COMPILER let index = Internals.FindSliceIndex.seqImpl slice source #else @@ -268,7 +314,10 @@ module Seq = /// /// The index of the slice. /// - let findLastSliceIndex (slice: seq<_>) (source: seq<_>) = + let findLastSliceIndex (slice: seq<'T>) (source: seq<'T>) : int = + let slice = nullArgCheck (nameof slice) slice + let source = nullArgCheck (nameof source) source + #if !FABLE_COMPILER let index = Internals.FindLastSliceIndex.seqImpl slice source #else @@ -290,7 +339,10 @@ module Seq = /// /// The index of the slice or None. /// - let tryFindLastSliceIndex (slice: seq<_>) (source: seq<_>) = + let tryFindLastSliceIndex (slice: seq<'T>) (source: seq<'T>) : int option = + let slice = nullArgCheck (nameof slice) slice + let source = nullArgCheck (nameof source) source + #if !FABLE_COMPILER let index = Internals.FindLastSliceIndex.seqImpl slice source #else @@ -304,6 +356,8 @@ module Seq = /// The input seq. /// /// Seq with values x for each List value where the function returns Some(x). - let choosei mapping source = - Seq.indexed source + let choosei (mapping: int -> 'T -> 'U option) (source: seq<'T>) : seq<'U> = + source + |> nullArgCheck (nameof source) + |> Seq.indexed |> Seq.choose (fun (a, b) -> mapping a b) diff --git a/src/FSharpPlus/Extensions/String.fs b/src/FSharpPlus/Extensions/String.fs index 7eddcd68f..f18b241e5 100644 --- a/src/FSharpPlus/Extensions/String.fs +++ b/src/FSharpPlus/Extensions/String.fs @@ -10,77 +10,77 @@ module String = /// Concatenates all elements, using the specified separator between each element. let intercalate (separator: string) (source: seq) = - raiseIfNull (nameof separator) separator - raiseIfNull (nameof source) source + let separator = nullArgCheck (nameof separator) separator + let source = nullArgCheck (nameof source) source String.Join (separator, source) /// Inserts a separator char between each char in the source string. let intersperse (element: char) (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source String.Join ("", Array.ofSeq (source |> Seq.intersperse element)) /// Creates a sequence of strings by splitting the source string on any of the given separators. let split (separators: seq) (source: string) = - raiseIfNull (nameof separators) separators - raiseIfNull (nameof source) source + let separators = nullArgCheck (nameof separators) separators + let source = nullArgCheck (nameof source) source source.Split (Seq.toArray separators, StringSplitOptions.None) :> seq<_> /// Replaces a substring with the given replacement string. let replace (oldValue: string) newValue (source: string) = - raiseIfNull (nameof oldValue) oldValue - raiseIfNull (nameof source) source + let oldValue = nullArgCheck (nameof oldValue) oldValue + let source = nullArgCheck (nameof source) source if oldValue.Length = 0 then source else source.Replace (oldValue, newValue) /// Does the source string contain the given subString? -- function wrapper for String.Contains method. let isSubString (subString: string) (source: string) = - raiseIfNull (nameof subString) subString - raiseIfNull (nameof source) source + let subString = nullArgCheck (nameof subString) subString + let source = nullArgCheck (nameof source) source source.Contains subString /// Does the source string start with the given subString? -- function wrapper for String.StartsWith method using InvariantCulture. let startsWith (subString: string) (source: string) = - raiseIfNull (nameof subString) subString - raiseIfNull (nameof source) source + let subString = nullArgCheck (nameof subString) subString + let source = nullArgCheck (nameof source) source source.StartsWith (subString, false, CultureInfo.InvariantCulture) /// Does the source string end with the given subString? -- function wrapper for String.EndsWith method using InvariantCulture. - let endsWith subString (source: string) = - raiseIfNull (nameof subString) subString - raiseIfNull (nameof source) source + let endsWith (subString: string) (source: string) = + let subString = nullArgCheck (nameof subString) subString + let source = nullArgCheck (nameof source) source source.EndsWith (subString, false, CultureInfo.InvariantCulture) /// Does the source string contain the given character? /// Use `String.isSubstring` to check for strings. let contains char (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source Seq.contains char source /// Converts to uppercase -- function wrapper for String.ToUpperInvariant method. let toUpper (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source - if isNull source then source else source.ToUpperInvariant () + source.ToUpperInvariant () /// Converts to lowercase -- function wrapper for String.ToLowerInvariant method. let toLower (source: string) = - raiseIfNull (nameof source) source - - if isNull source then source else source.ToLowerInvariant () + let source = nullArgCheck (nameof source) source + + source.ToLowerInvariant () /// Trims leading and trailing white spaces -- function wrapper for String.Trim method. /// /// Note this is distinct from trim which trims the given characters, /// not white spaces. let trim (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source.Trim () @@ -89,7 +89,7 @@ module String = /// Note this is distinct from trim which trims the given characters, /// not white spaces. let trimStartWhiteSpaces (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source.TrimStart () @@ -98,7 +98,7 @@ module String = /// Note this is distinct from trim which trims the given characters, /// not white spaces. let trimEndWhiteSpaces (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source.TrimEnd () @@ -106,8 +106,11 @@ module String = /// Returns a new string whose textual value is the same as this string, but whose binary representation is in the specified Unicode normalization form. /// - /// This is a null safe function wrapper of the String.Normalize method. - let normalize normalizationForm (source: string) = if isNull source then source else source.Normalize normalizationForm + /// This is a function wrapper of the String.Normalize method. + let normalize normalizationForm (source: string) = + let source = nullArgCheck (nameof source) source + + source.Normalize normalizationForm /// Removes diacritics (accents) from the given source string. /// @@ -115,71 +118,71 @@ module String = /// (basically separating the "base" characters from the diacritics) and then scans /// the result and retains only the base characters. let removeDiacritics (source: string) = - if isNull source then source - else - source - |> normalize NormalizationForm.FormD - |> String.filter (fun ch -> CharUnicodeInfo.GetUnicodeCategory ch <> UnicodeCategory.NonSpacingMark) - |> normalize NormalizationForm.FormC + let source = nullArgCheck (nameof source) source + + source + |> normalize NormalizationForm.FormD + |> String.filter (fun ch -> CharUnicodeInfo.GetUnicodeCategory ch <> UnicodeCategory.NonSpacingMark) + |> normalize NormalizationForm.FormC #endif /// Pads the beginning of the given string with spaces so that it has a specified total length. let padLeft totalLength (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source.PadLeft totalLength /// Pads the beginning of the given string with a specified character so that it has a specified total length. let padLeftWith totalLength paddingChar (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source.PadLeft (totalLength, paddingChar) /// Pads the end of the given string with spaces so that it has a specified total length. let padRight totalLength (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source.PadRight totalLength /// Pads the end of the given string with a specified character so that it has a specified total length. let padRightWith totalLength paddingChar (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source.PadRight (totalLength, paddingChar) /// Removes all leading and trailing occurrences of specified characters from the given string. let trimBoth (trimChars: char seq) (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source.Trim (Seq.toArray trimChars) /// Removes all leading occurrences of specified characters from the given string. let trimStart (trimChars: char seq) (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source.TrimStart (Seq.toArray trimChars) /// Removes all trailing occurrences of specified characters from the given string. let trimEnd (trimChars: char seq) (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source.TrimEnd (Seq.toArray trimChars) /// Converts the given string to an array of chars. let toArray (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source.ToCharArray () /// Converts an array of chars to a String. let ofArray (source: char []) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source String (source) /// Converts the given string to a list of chars. let toList (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source toArray source |> List.ofArray @@ -188,13 +191,13 @@ module String = /// Converts the given string to a seq of chars. let toSeq (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source :> seq /// Converts a seq of chars to a String. let ofSeq (source: seq) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source String.Join (String.Empty, source) @@ -205,20 +208,20 @@ module String = /// Note: this is not exception safe, and will throw System.IndexOutOfRangeException when /// the given index is out of bounds. let item (index: int) (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source.[index] /// Returns the char (as an Option) at the given index in the source string, /// returning `None` if out of bounds. let tryItem (index: int) (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source if index >= 0 && index < source.Length then Some source.[index] else None /// Reverses the given string. let rev (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source String (source.ToCharArray () |> Array.rev) @@ -228,7 +231,7 @@ module String = /// Note: will throw System.ArgumentOutOfRangeException if you try to take more than the /// number of chars in the string. let take count (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source[..count-1] @@ -238,13 +241,13 @@ module String = /// Note: will throw System.ArgumentOutOfRangeException if you try to skip more than the /// number of chars in the string. let skip count (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source[count..] /// Takes chars from the source string while the given predicate is true. let takeWhile (predicate: char -> bool) (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source if String.IsNullOrEmpty source then String.Empty @@ -258,7 +261,7 @@ module String = /// Skips over chars from the source string while the given predicate is true. let skipWhile (predicate: char -> bool) (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source if String.IsNullOrEmpty source then String.Empty @@ -273,14 +276,14 @@ module String = /// Gets the first char of the string, or /// None if the string is empty. let tryHead (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source if String.length source = 0 then None else Some source.[0] /// Gets the last char of the string, or /// None if the string is empty. let tryLast (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source let length = String.length source if length = 0 then None else Some source.[length-1] @@ -288,7 +291,7 @@ module String = /// Returns a string that has at most N characters from the beginning of the original string. /// It returns the original string if it is shorter than count. let truncate count (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source if count < 1 then String.Empty else if String.length source <= count then source @@ -297,7 +300,7 @@ module String = /// Returns a string that drops first N characters of the original string. /// When count exceeds the length of the string it returns an empty string. let drop count (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source if count < 1 then source else if String.length source <= count then String.Empty @@ -307,7 +310,7 @@ module String = /// /// Note: throws an ArgumentException when not found. let findIndex (predicate: char -> bool) (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source let rec go index = if index >= source.Length then @@ -318,7 +321,7 @@ module String = /// Tries to find the first index of the char in the substring which satisfies the given predicate. let tryFindIndex (predicate: char -> bool) (source: string) = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source let rec go index = if index >= source.Length then None @@ -336,8 +339,8 @@ module String = /// The index of the slice. /// let findSliceIndex (slice: string) (source: string) = - raiseIfNull (nameof slice) slice - raiseIfNull (nameof source) source + let slice = nullArgCheck (nameof slice) slice + let source = nullArgCheck (nameof source) source let index = source.IndexOf slice if index = -1 then @@ -369,8 +372,8 @@ module String = /// The index of the slice or None. /// let tryFindSliceIndex (slice: string) (source: string) = - raiseIfNull (nameof slice) slice - raiseIfNull (nameof source) source + let slice = nullArgCheck (nameof slice) slice + let source = nullArgCheck (nameof source) source let index = source.IndexOf slice if index = -1 then None else Some index @@ -390,7 +393,7 @@ module String = /// Converts the given string to an array of Int32 code-points (the actual Unicode Code Point number). let toCodePoints (source : string) : seq = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source let mapper i c = // Ignore the low-surrogate because it's already been converted @@ -400,14 +403,14 @@ module String = /// Converts the array of Int32 code-points (the actual Unicode Code Point number) to a string. let ofCodePoints (source: seq) : string = - raiseIfNull (nameof source) source + let source = nullArgCheck (nameof source) source source |> Seq.map Char.ConvertFromUtf32 |> String.concat String.Empty #endif /// Converts a string to a byte-array using the specified encoding. let getBytes (encoding: Encoding) (source: string) : byte [] = - raiseIfNull (nameof encoding) encoding - raiseIfNull (nameof source) source + let encoding = nullArgCheck (nameof encoding) encoding + let source = nullArgCheck (nameof source) source encoding.GetBytes source diff --git a/src/FSharpPlus/Extensions/Task.fs b/src/FSharpPlus/Extensions/Task.fs index 66a3fe79a..1ca8a6628 100644 --- a/src/FSharpPlus/Extensions/Task.fs +++ b/src/FSharpPlus/Extensions/Task.fs @@ -9,14 +9,17 @@ module Task = open System open System.Threading open System.Threading.Tasks + open FSharpPlus.Internals.Errors let private (|Canceled|Faulted|Completed|) (t: Task<'a>) = if t.IsCanceled then Canceled - else if t.IsFaulted then Faulted t.Exception + else if t.IsFaulted then Faulted (Unchecked.nonNull t.Exception) else Completed t.Result /// Creates a task workflow from 'source' another, mapping its result with 'f'. let map (f: 'T -> 'U) (source: Task<'T>) : Task<'U> = + let source = nullArgCheck (nameof source) source + if source.Status = TaskStatus.RanToCompletion then try Task.FromResult (f source.Result) with e -> @@ -26,7 +29,7 @@ module Task = else let tcs = TaskCompletionSource<'U> () if source.Status = TaskStatus.Faulted then - tcs.SetException source.Exception.InnerExceptions + tcs.SetException (Unchecked.nonNull source.Exception).InnerExceptions tcs.Task elif source.Status = TaskStatus.Canceled then tcs.SetCanceled () @@ -47,6 +50,9 @@ module Task = /// First task workflow. /// Second task workflow. let lift2 (f: 'T -> 'U -> 'V) (x: Task<'T>) (y: Task<'U>) : Task<'V> = + let x = nullArgCheck (nameof x) x + let y = nullArgCheck (nameof y) y + if x.Status = TaskStatus.RanToCompletion && y.Status = TaskStatus.RanToCompletion then try Task.FromResult (f x.Result y.Result) with e -> @@ -57,9 +63,9 @@ module Task = let tcs = TaskCompletionSource<'V> () match x.Status, y.Status with | TaskStatus.Canceled, _ -> tcs.SetCanceled () - | TaskStatus.Faulted, _ -> tcs.SetException x.Exception.InnerExceptions + | TaskStatus.Faulted, _ -> tcs.SetException (Unchecked.nonNull x.Exception).InnerExceptions | _, TaskStatus.Canceled -> tcs.SetCanceled () - | _, TaskStatus.Faulted -> tcs.SetException y.Exception.InnerExceptions + | _, TaskStatus.Faulted -> tcs.SetException (Unchecked.nonNull y.Exception).InnerExceptions | TaskStatus.RanToCompletion, _ -> let k = function | Canceled -> tcs.SetCanceled () @@ -98,22 +104,26 @@ module Task = /// First task workflow. /// Second task workflow. /// Third task workflow. - let lift3 (f : 'T -> 'U -> 'V -> 'W) (x : Task<'T>) (y : Task<'U>) (z: Task<'V>) : Task<'W> = + let lift3 (f : 'T1 -> 'T2 -> 'T3 -> 'U) (x : Task<'T1>) (y : Task<'T2>) (z: Task<'T3>) : Task<'U> = + let x = nullArgCheck (nameof x) x + let y = nullArgCheck (nameof y) y + let z = nullArgCheck (nameof z) z + if x.Status = TaskStatus.RanToCompletion && y.Status = TaskStatus.RanToCompletion && z.Status = TaskStatus.RanToCompletion then try Task.FromResult (f x.Result y.Result z.Result) with e -> - let tcs = TaskCompletionSource<'W> () + let tcs = TaskCompletionSource<'U> () tcs.SetException e tcs.Task else - let tcs = TaskCompletionSource<'W> () + let tcs = TaskCompletionSource<'U> () match x.Status, y.Status, z.Status with | TaskStatus.Canceled, _ , _ -> tcs.SetCanceled () - | TaskStatus.Faulted , _ , _ -> tcs.SetException x.Exception.InnerExceptions + | TaskStatus.Faulted , _ , _ -> tcs.SetException (Unchecked.nonNull x.Exception).InnerExceptions | _ , TaskStatus.Canceled, _ -> tcs.SetCanceled () - | _ , TaskStatus.Faulted , _ -> tcs.SetException y.Exception.InnerExceptions + | _ , TaskStatus.Faulted , _ -> tcs.SetException (Unchecked.nonNull y.Exception).InnerExceptions | _ , _ , TaskStatus.Canceled -> tcs.SetCanceled () - | _ , _ , TaskStatus.Faulted -> tcs.SetException z.Exception.InnerExceptions + | _ , _ , TaskStatus.Faulted -> tcs.SetException (Unchecked.nonNull z.Exception).InnerExceptions | _ , _ , _ -> x.ContinueWith ( function @@ -143,6 +153,9 @@ module Task = /// First Task workflow. /// Second Task workflow. let map2 mapper (task1: Task<'T1>) (task2: Task<'T2>) : Task<'U> = + let task1 = nullArgCheck (nameof task1) task1 + let task2 = nullArgCheck (nameof task2) task2 + if task1.Status = TaskStatus.RanToCompletion && task2.Status = TaskStatus.RanToCompletion then try Task.FromResult (mapper task1.Result task2.Result) with e -> @@ -190,6 +203,10 @@ module Task = /// Second Task workflow. /// Third Task workflow. let map3 mapper (task1: Task<'T1>) (task2: Task<'T2>) (task3: Task<'T3>) : Task<'U> = + let task1 = nullArgCheck (nameof task1) task1 + let task2 = nullArgCheck (nameof task2) task2 + let task3 = nullArgCheck (nameof task3) task3 + if task1.Status = TaskStatus.RanToCompletion && task2.Status = TaskStatus.RanToCompletion && task3.Status = TaskStatus.RanToCompletion then try Task.FromResult (mapper task1.Result task2.Result task3.Result) with e -> @@ -236,6 +253,9 @@ module Task = /// Task workflow returning a function /// Task workflow returning a value let apply (f: Task<'T->'U>) (x: Task<'T>) : Task<'U> = + let f = nullArgCheck (nameof f) f + let x = nullArgCheck (nameof x) x + if f.Status = TaskStatus.RanToCompletion && x.Status = TaskStatus.RanToCompletion then try Task.FromResult (f.Result x.Result) with e -> @@ -246,9 +266,9 @@ module Task = let tcs = TaskCompletionSource<'U> () match f.Status, x.Status with | TaskStatus.Canceled, _ -> tcs.SetCanceled () - | TaskStatus.Faulted, _ -> tcs.SetException f.Exception.InnerExceptions + | TaskStatus.Faulted, _ -> tcs.SetException (Unchecked.nonNull f.Exception).InnerExceptions | _, TaskStatus.Canceled -> tcs.SetCanceled () - | _, TaskStatus.Faulted -> tcs.SetException x.Exception.InnerExceptions + | _, TaskStatus.Faulted -> tcs.SetException (Unchecked.nonNull x.Exception).InnerExceptions | TaskStatus.RanToCompletion, _ -> let k = function | Canceled -> tcs.SetCanceled () @@ -283,15 +303,18 @@ module Task = /// Creates a task workflow from two workflows 'x' and 'y', tupling its results. let zipSequentially (x: Task<'T>) (y: Task<'U>) : Task<'T * 'U> = + let x = nullArgCheck (nameof x) x + let y = nullArgCheck (nameof y) y + if x.Status = TaskStatus.RanToCompletion && y.Status = TaskStatus.RanToCompletion then Task.FromResult (x.Result, y.Result) else let tcs = TaskCompletionSource<'T * 'U> () match x.Status, y.Status with | TaskStatus.Canceled, _ -> tcs.SetCanceled () - | TaskStatus.Faulted, _ -> tcs.SetException x.Exception.InnerExceptions + | TaskStatus.Faulted, _ -> tcs.SetException (Unchecked.nonNull x.Exception).InnerExceptions | _, TaskStatus.Canceled -> tcs.SetCanceled () - | _, TaskStatus.Faulted -> tcs.SetException y.Exception.InnerExceptions + | _, TaskStatus.Faulted -> tcs.SetException (Unchecked.nonNull y.Exception).InnerExceptions | TaskStatus.RanToCompletion, _ -> let k = function | Canceled -> tcs.SetCanceled () @@ -329,25 +352,30 @@ module Task = let zip3 (task1: Task<'T1>) (task2: Task<'T2>) (task3: Task<'T3>) = map3 (fun x y z -> x, y, z) task1 task2 task3 /// Flattens two nested tasks into one. - let join (source: Task>) : Task<'T> = source.Unwrap() + let join (source: Task>) : Task<'T> = + let source = nullArgCheck (nameof source) source + + source.Unwrap() /// Creates a task workflow from 'source' workflow, mapping and flattening its result with 'f'. - let bind (f: 'T -> Task<'U>) (source: Task<'T>) : Task<'U> = source |> map f |> join + let bind (f: 'T -> Task<'U>) (source: Task<'T>) : Task<'U> = source |> Unchecked.nonNull |> map f |> join /// Creates a task that ignores the result of the source task. /// It can be used to convert non-generic Task to unit Task. let ignore (task: Task) = + let task = nullArgCheck (nameof task) task + if task.Status = TaskStatus.RanToCompletion then Task.FromResult () else let tcs = TaskCompletionSource () if task.Status = TaskStatus.Faulted then - tcs.SetException task.Exception.InnerExceptions + tcs.SetException (Unchecked.nonNull task.Exception).InnerExceptions elif task.Status = TaskStatus.Canceled then tcs.SetCanceled () else let k (t: Task) : unit = if t.IsCanceled then tcs.SetCanceled () - elif t.IsFaulted then tcs.SetException t.Exception + elif t.IsFaulted then tcs.SetException (Unchecked.nonNull t.Exception).InnerExceptions else tcs.SetResult () task.ContinueWith k |> ignore tcs.Task @@ -361,7 +389,7 @@ module Task = let task = body () match task.Status with | TaskStatus.RanToCompletion -> task - | TaskStatus.Faulted -> task.ContinueWith((fun (x:Task<'T>) -> compensation (unwrapException x.Exception))).Unwrap () + | TaskStatus.Faulted -> task.ContinueWith((fun (x:Task<'T>) -> compensation (unwrapException (Unchecked.nonNull x.Exception)))).Unwrap () | TaskStatus.Canceled -> task | _ -> task.ContinueWith((fun (x:Task<'T>) -> tryWith (fun () -> x) compensation) ).Unwrap () with @@ -389,13 +417,13 @@ module Task = reraise () /// Used to de-sugar use .. blocks in Computation Expressions. - let using (disp: #IDisposable) (body: _ -> Task<'a>) = + let using (disp: 'T when 'T :> IDisposable) (body: 'T -> Task<'U>) = tryFinally (fun () -> body disp) (fun () -> if not (isNull (box disp)) then disp.Dispose ()) /// Creates a Task from a value - let result value = Task.FromResult value + let result (value: 'T) = Task.FromResult value /// Raises an exception in the Task let raise<'T> (e: exn) = diff --git a/src/FSharpPlus/Extensions/ValueTask.fs b/src/FSharpPlus/Extensions/ValueTask.fs index cc0152070..952c4a34c 100644 --- a/src/FSharpPlus/Extensions/ValueTask.fs +++ b/src/FSharpPlus/Extensions/ValueTask.fs @@ -9,13 +9,14 @@ module ValueTask = open System open System.Threading open System.Threading.Tasks + open FSharpPlus.Internals.Errors - let inline internal (|Succeeded|Canceled|Faulted|) (t: ValueTask<'T>) = + let inline (|Succeeded|Canceled|Faulted|) (t: ValueTask<'T>) = if t.IsCompletedSuccessfully then Succeeded t.Result elif t.IsCanceled then Canceled - else Faulted (t.AsTask().Exception) + else Faulted (t.AsTask().Exception |> Unchecked.nonNull) - let inline internal continueTask (tcs: TaskCompletionSource<'Result>) (x: ValueTask<'t>) (k: 't -> unit) = + let inline continueTask (tcs: TaskCompletionSource<'Result>) (x: ValueTask<'t>) (k: 't -> unit) = let f = function | Succeeded r -> k r | Canceled -> tcs.SetCanceled () @@ -23,7 +24,7 @@ module ValueTask = if x.IsCompleted then f x else x.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted (fun () -> f x) - let inline internal continueWith (x: ValueTask<'t>) f = + let inline continueWith (x: ValueTask<'t>) f = if x.IsCompleted then f x else x.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted (fun () -> f x) diff --git a/src/FSharpPlus/Internals.fs b/src/FSharpPlus/Internals.fs index d3bf01922..d84400139 100644 --- a/src/FSharpPlus/Internals.fs +++ b/src/FSharpPlus/Internals.fs @@ -49,9 +49,12 @@ module Errors = let exnNoSubtraction = new System.Exception "No subtraction defined for these values in this domain." let exnUnreachable = new System.InvalidOperationException "This execution path is unreachable." - let inline raiseIfNull paramName paramValue = - if isNull paramValue then - nullArg paramName + // Functions to remove when compiling with F#9 or higher + let inline nullArgCheck paramName paramValue = + if isNull paramValue then nullArg paramName + else paramValue + + module Unchecked = let nonNull = id module Decimal = let inline trySqrt x = @@ -145,6 +148,7 @@ type NonEmptySeq2<'T> = #nowarn "51" open System open Microsoft.FSharp.NativeInterop +open Errors // TODO: see if it makes sense to move checks to calling site type BitConverter = /// Converts a byte into an array of bytes with length one. @@ -227,7 +231,7 @@ type BitConverter = /// Converts an array of bytes into a short. static member ToInt16 (value: byte[], startIndex: int, isLittleEndian: bool) = - if isNull value then nullArg "value" + let value = nullArgCheck (nameof value) value if startIndex >= value.Length then raise <| new ArgumentOutOfRangeException ("startIndex", "ArgumentOutOfRange_Index") if startIndex > value.Length - 2 then raise <| new ArgumentException "Arg_ArrayPlusOffTooSmall" use pbyte = fixed &value.[startIndex] @@ -239,7 +243,7 @@ type BitConverter = /// Converts an array of bytes into an int. static member ToInt32 (value: byte[], startIndex: int, isLittleEndian: bool) : int = - if isNull value then nullArg "value" + let value = nullArgCheck (nameof value) value if startIndex >= value.Length then raise <| new ArgumentOutOfRangeException ("startIndex", "ArgumentOutOfRange_Index") if startIndex > value.Length - 4 then raise <| new ArgumentException "Arg_ArrayPlusOffTooSmall" use pbyte = fixed &value.[startIndex] @@ -251,7 +255,7 @@ type BitConverter = /// Converts an array of bytes into a long. static member ToInt64 (value: byte[], startIndex: int, isLittleEndian: bool) = - if isNull value then nullArg "value" + let value = nullArgCheck (nameof value) value if startIndex >= value.Length then raise <| new ArgumentOutOfRangeException ("startIndex", "ArgumentOutOfRange_Index") if startIndex > value.Length - 8 then raise <| new ArgumentException "Arg_ArrayPlusOffTooSmall" use pbyte = fixed &value.[startIndex] @@ -268,7 +272,7 @@ type BitConverter = i2 ||| (i1 <<< 32) static member ToGuid (value: byte[], startIndex: int, isLittleEndian: bool) = - if isNull value then nullArg "value" + let value = nullArgCheck (nameof value) value if startIndex >= value.Length then raise <| new ArgumentOutOfRangeException ("startIndex", "ArgumentOutOfRange_Index") if startIndex > value.Length - 16 then raise <| new ArgumentException "Arg_ArrayPlusOffTooSmall" if isLittleEndian then @@ -311,7 +315,7 @@ type BitConverter = /// Converts an array of bytes into a String. static member ToString (value: byte [], startIndex, length) = - if isNull value then nullArg "value" + let value = nullArgCheck (nameof value) value let arrayLen = value.Length if startIndex >= value.Length then raise <| new ArgumentOutOfRangeException ("startIndex", "ArgumentOutOfRange_StartIndex") let realLength = length @@ -333,12 +337,12 @@ type BitConverter = /// Converts an array of bytes into a String. static member ToString (value: byte []) = - if isNull value then nullArg "value" + let value = nullArgCheck (nameof value) value BitConverter.ToString (value, 0, value.Length) /// Converts an array of bytes into a String. static member ToString (value: byte [], startIndex) = - if isNull value then nullArg "value" + let value = nullArgCheck (nameof value) value BitConverter.ToString (value, startIndex, value.Length - startIndex) #if !FABLE_COMPILER