From 5123a3e51fcc731613fc421091cd6b810ca73321 Mon Sep 17 00:00:00 2001 From: Gustavo Leon <1261319+gusty@users.noreply.github.com> Date: Thu, 26 Nov 2020 11:57:38 +0100 Subject: [PATCH 01/15] +Dict.initInfinite --- src/FSharpPlus/Extensions/Dict.fs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/FSharpPlus/Extensions/Dict.fs b/src/FSharpPlus/Extensions/Dict.fs index 490e308d2..f6fdca1ab 100644 --- a/src/FSharpPlus/Extensions/Dict.fs +++ b/src/FSharpPlus/Extensions/Dict.fs @@ -161,3 +161,29 @@ module Dict = | Some v -> dct.Add (k, v) | None -> () dct :> IDictionary<'Key, 'U> + + /// Creates a conceptually infinite dictionay containing the same value for all possible keys. + /// The value for all possible keys. + let initInfinite<'TKey,'TValue> (source: 'TValue) : IDictionary<'TKey,'TValue> = + { + new IDictionary<'TKey,'TValue> with + member __.TryGetValue (_key: 'TKey, value: byref<'TValue>) = value <- source; true + member __.Count = System.Int32.MaxValue + member __.ContainsKey (_key: 'TKey) = true + member __.GetEnumerator () = Seq.empty.GetEnumerator () :> System.Collections.IEnumerator + member __.GetEnumerator () = Seq.empty.GetEnumerator () : IEnumerator> + member __.IsReadOnly = true + member __.Item + with get (_key: 'TKey) : 'TValue = source + and set (_key: 'TKey) (_: 'TValue) : unit = raise (System.NotImplementedException()) + + member __.Add (_key: 'TKey, _value: 'TValue) : unit = raise (System.NotImplementedException()) + member __.Add (_item: KeyValuePair<'TKey,'TValue>) : unit = raise (System.NotImplementedException()) + member __.Clear () : unit = raise (System.NotImplementedException()) + member __.Contains (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException()) + member __.CopyTo (_arr: KeyValuePair<'TKey,'TValue> [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) + member __.Keys : ICollection<'TKey> = raise (System.NotImplementedException()) + member __.Remove (_key: 'TKey) : bool = raise (System.NotImplementedException()) + member __.Remove (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException()) + member __.Values : ICollection<'TValue> = raise (System.NotImplementedException()) + } From 6c30108f62de73619b2b9d273ea576c23b1b7783 Mon Sep 17 00:00:00 2001 From: Gustavo Leon <1261319+gusty@users.noreply.github.com> Date: Thu, 26 Nov 2020 11:59:39 +0100 Subject: [PATCH 02/15] + Dict overload for Return --- src/FSharpPlus/Control/Monad.fs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/FSharpPlus/Control/Monad.fs b/src/FSharpPlus/Control/Monad.fs index cfbc24936..68e7f2467 100644 --- a/src/FSharpPlus/Control/Monad.fs +++ b/src/FSharpPlus/Control/Monad.fs @@ -136,6 +136,7 @@ type Return = static member Return (_: 'a Async , _: Return ) = fun (x: 'a) -> async.Return x static member Return (_: Result<'a,'e> , _: Return ) = fun x -> Ok x : Result<'a,'e> static member Return (_: Choice<'a,'e> , _: Return ) = fun x -> Choice1Of2 x : Choice<'a,'e> + static member Return (_: IDictionary<'k,'t>, _: Return) = fun x -> Dict.initInfinite x : IDictionary<'k,'t> static member Return (_: Expr<'a> , _: Return ) = fun x -> Expr.Cast<'a> (Expr.Value (x: 'a)) static member Return (_: ResizeArray<'a>, _: Return ) = fun x -> ResizeArray<'a> (Seq.singleton x) @@ -276,4 +277,4 @@ type Using with static member inline Using (resource: 'T when 'T :> IDisposable, body: 'T -> '``Monad<'U>`` , _: Using ) = Using.InvokeOnInstance resource body : '``Monad<'U>`` static member inline Using (_ , _ : 'a -> ^t when ^t : null and ^t: struct , _: Using ) = () - #endif \ No newline at end of file + #endif From 0bd3646a7f8b7904378606c06baf88e05ad2a37a Mon Sep 17 00:00:00 2001 From: Gustavo Leon <1261319+gusty@users.noreply.github.com> Date: Thu, 26 Nov 2020 12:01:14 +0100 Subject: [PATCH 03/15] +Dict overload for <*> --- src/FSharpPlus/Control/Applicative.fs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/FSharpPlus/Control/Applicative.fs b/src/FSharpPlus/Control/Applicative.fs index d9e1db180..fc7a569fe 100644 --- a/src/FSharpPlus/Control/Applicative.fs +++ b/src/FSharpPlus/Control/Applicative.fs @@ -47,6 +47,14 @@ type Apply = | true, vx -> dct.Add (k, vf vx) | _ -> () dct + + static member ``<*>`` (f: IDictionary<'Key,_>, x: IDictionary<'Key,'T> , []_output: IDictionary<'Key,'U> , []_mthd: Apply) : IDictionary<'Key,'U> = + let dct = Dictionary () + for KeyValue(k, vx) in x do + match f.TryGetValue k with + | true, vf -> dct.Add (k, vf vx) + | _ -> () + dct :> IDictionary<'Key,'U> static member ``<*>`` (f: Expr<'T->'U>, x: Expr<'T>, []_output: Expr<'U>, []_mthd: Apply) = Expr.Cast<'U> (Expr.Application (f, x)) static member ``<*>`` (f: ('T->'U) ResizeArray, x: 'T ResizeArray, []_output: 'U ResizeArray, []_mthd: Apply) = ResizeArray.apply f x : 'U ResizeArray @@ -131,4 +139,4 @@ type IsLeftZero with static member inline IsLeftZero (t: ref<'``Applicative<'T>``> , _mthd: Default1) = (^``Applicative<'T>`` : (static member IsLeftZero : _ -> _) t.Value) static member inline IsLeftZero (_: ref< ^t> when ^t: null and ^t: struct, _: Default1) = () -#endif \ No newline at end of file +#endif From 3407b816bff47aa0d54d9cebb7461b545c5aba32 Mon Sep 17 00:00:00 2001 From: Gustavo Leon <1261319+gusty@users.noreply.github.com> Date: Fri, 27 Nov 2020 09:22:49 +0100 Subject: [PATCH 04/15] Implement Values and Contains --- src/FSharpPlus/Extensions/Dict.fs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/FSharpPlus/Extensions/Dict.fs b/src/FSharpPlus/Extensions/Dict.fs index f6fdca1ab..57655113e 100644 --- a/src/FSharpPlus/Extensions/Dict.fs +++ b/src/FSharpPlus/Extensions/Dict.fs @@ -165,14 +165,32 @@ module Dict = /// Creates a conceptually infinite dictionay containing the same value for all possible keys. /// The value for all possible keys. let initInfinite<'TKey,'TValue> (source: 'TValue) : IDictionary<'TKey,'TValue> = - { + + let icollection value = + { + new ICollection<'t> with + member __.Contains (item: 't) = obj.ReferenceEquals (item, value) + member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () :> System.Collections.IEnumerator + member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () : IEnumerator<'t> + member __.IsReadOnly = true + + member __.Add (_item: 't) : unit = raise (System.NotImplementedException()) + member __.Clear () : unit = raise (System.NotImplementedException()) + member __.CopyTo (_array: 't [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) + member __.Count : int = raise (System.NotImplementedException()) + member __.Remove (_item: 't): bool = raise (System.NotImplementedException()) + } + + { new IDictionary<'TKey,'TValue> with member __.TryGetValue (_key: 'TKey, value: byref<'TValue>) = value <- source; true member __.Count = System.Int32.MaxValue member __.ContainsKey (_key: 'TKey) = true + member __.Contains (item: KeyValuePair<'TKey,'TValue>) = obj.ReferenceEquals (item.Value, source) member __.GetEnumerator () = Seq.empty.GetEnumerator () :> System.Collections.IEnumerator member __.GetEnumerator () = Seq.empty.GetEnumerator () : IEnumerator> member __.IsReadOnly = true + member __.Values = icollection source member __.Item with get (_key: 'TKey) : 'TValue = source and set (_key: 'TKey) (_: 'TValue) : unit = raise (System.NotImplementedException()) @@ -180,10 +198,8 @@ module Dict = member __.Add (_key: 'TKey, _value: 'TValue) : unit = raise (System.NotImplementedException()) member __.Add (_item: KeyValuePair<'TKey,'TValue>) : unit = raise (System.NotImplementedException()) member __.Clear () : unit = raise (System.NotImplementedException()) - member __.Contains (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException()) member __.CopyTo (_arr: KeyValuePair<'TKey,'TValue> [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) member __.Keys : ICollection<'TKey> = raise (System.NotImplementedException()) member __.Remove (_key: 'TKey) : bool = raise (System.NotImplementedException()) member __.Remove (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException()) - member __.Values : ICollection<'TValue> = raise (System.NotImplementedException()) } From 25c00cdc00e2b9bf9a9cd3f945ee7dfa45ef2273 Mon Sep 17 00:00:00 2001 From: Gusty <1261319+gusty@users.noreply.github.com> Date: Fri, 4 Jun 2021 19:56:19 +0200 Subject: [PATCH 05/15] fix enumeration + tests --- src/FSharpPlus/Extensions/Dict.fs | 4 ++-- tests/FSharpPlus.Tests/General.fs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/FSharpPlus/Extensions/Dict.fs b/src/FSharpPlus/Extensions/Dict.fs index 57655113e..c145e8f6a 100644 --- a/src/FSharpPlus/Extensions/Dict.fs +++ b/src/FSharpPlus/Extensions/Dict.fs @@ -187,8 +187,8 @@ module Dict = member __.Count = System.Int32.MaxValue member __.ContainsKey (_key: 'TKey) = true member __.Contains (item: KeyValuePair<'TKey,'TValue>) = obj.ReferenceEquals (item.Value, source) - member __.GetEnumerator () = Seq.empty.GetEnumerator () :> System.Collections.IEnumerator - member __.GetEnumerator () = Seq.empty.GetEnumerator () : IEnumerator> + member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : System.Collections.IEnumerator + member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : IEnumerator> member __.IsReadOnly = true member __.Values = icollection source member __.Item diff --git a/tests/FSharpPlus.Tests/General.fs b/tests/FSharpPlus.Tests/General.fs index 0d33480c0..e97a298ac 100644 --- a/tests/FSharpPlus.Tests/General.fs +++ b/tests/FSharpPlus.Tests/General.fs @@ -436,6 +436,18 @@ module Functor = Assert.IsInstanceOf>> (Some testVal10) areEqual 2 (testVal10 |> Async.RunSynchronously) + let testVal11 = (+) "h" dict [1, "i"; 2, "ello"] + CollectionAssert.AreEqual (dict [(1, "hi"); (2, "hello")], testVal11) + + let testVal12 = + let h: IDictionary = result "h" + try + (+) h <*> dict [1, "i"; 2, "ello"] + with _ -> dict [0, "failure"] + CollectionAssert.AreEqual (dict [0, "failure"], testVal12) + + + [] let unzip () = let testVal = unzip {Head = (1, 'a'); Tail = [(2, 'b');(3, 'b')]} From bfa151d6c1de29f6141ad93bb2ce48bf66bcc77c Mon Sep 17 00:00:00 2001 From: gusty <1261319+gusty@users.noreply.github.com> Date: Tue, 19 Dec 2023 10:10:07 +0100 Subject: [PATCH 06/15] Change overload priority for OfSeq / OfList --- src/FSharpPlus/Control/Collection.fs | 26 +++++++++++++------------- tests/FSharpPlus.Tests/Collections.fs | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/FSharpPlus/Control/Collection.fs b/src/FSharpPlus/Control/Collection.fs index 9a832ee7b..04f033596 100644 --- a/src/FSharpPlus/Control/Collection.fs +++ b/src/FSharpPlus/Control/Collection.fs @@ -12,24 +12,24 @@ open FSharpPlus.Internals type OfSeq = inherit Default1 - - static member inline OfSeq ((x: seq<'t>, _: 'R), _: Default5) : 'R = + + static member inline OfSeq ((x: seq<'t>, _: '``Foldable'``), _: Default5) : '``Foldable'`` = #if TEST_TRACE - Traces.add "OfSeq, Default5-seq<'t>" + Traces.add "OfSeq, Return+Sum<'t>" #endif - (^R : (new : seq<'t> -> ^R) x) + x |> Seq.map Return.Invoke |> Sum.Invoke - static member inline OfSeq ((x: seq>, _: 'R), _: Default5) : 'R = + static member inline OfSeq ((x: seq<'t>, _: 'R), _: Default4) : 'R = #if TEST_TRACE - Traces.add "OfSeq, Default5-seq>" + Traces.add "OfSeq, #new seq<'t>" #endif - (^R : (new : seq<'k*'v> -> ^R) (Seq.map (|KeyValue|) x)) + (^R : (new : seq<'t> -> ^R) x) - static member inline OfSeq ((x: seq<'t>, _: '``Foldable'``), _: Default4) : '``Foldable'`` = + static member inline OfSeq ((x: seq>, _: 'R), _: Default4) : 'R = #if TEST_TRACE - Traces.add "OfSeq, Default4-seq<'t>" + Traces.add "OfSeq, #new seq>" #endif - x |> Seq.map Return.Invoke |> Sum.Invoke + (^R : (new : seq<'k*'v> -> ^R) (Seq.map (|KeyValue|) x)) static member OfSeq ((x: seq<'t> , _: seq<'t> ), _: Default3) = x static member OfSeq ((x: seq<'t> , _: ICollection<'t> ), _: Default3) = let d = ResizeArray () in Seq.iter d.Add x; d :> ICollection<'t> @@ -81,10 +81,10 @@ type OfSeq = type OfList = inherit Default1 - static member inline OfList ((x: list<'t> , _: 'R ), _: Default6) = (^R : (new : seq<'t> -> ^R) (List.toSeq x)) : 'R - static member inline OfList ((x: list>, _: 'R ), _: Default6) = (^R : (new : seq<'k*'v> -> ^R) (Seq.map (|KeyValue|) x)) : 'R + static member inline OfList ((x: list<'t> , _: '``Foldable'`` ), _: Default6) = x |> List.map Return.Invoke |> Sum.Invoke : '``Foldable'`` - static member inline OfList ((x: list<'t> , _: '``Foldable'`` ), _: Default5) = x |> List.map Return.Invoke |> Sum.Invoke : '``Foldable'`` + static member inline OfList ((x: list<'t> , _: 'R ), _: Default5) = (^R : (new : seq<'t> -> ^R) (List.toSeq x)) : 'R + static member inline OfList ((x: list>, _: 'R ), _: Default5) = (^R : (new : seq<'k*'v> -> ^R) (Seq.map (|KeyValue|) x)) : 'R static member OfList ((x: list<'t> , _: seq<'t> ), _: Default4) = List.toSeq x #if !FABLE_COMPILER diff --git a/tests/FSharpPlus.Tests/Collections.fs b/tests/FSharpPlus.Tests/Collections.fs index 297418e1e..ce9fd58f1 100644 --- a/tests/FSharpPlus.Tests/Collections.fs +++ b/tests/FSharpPlus.Tests/Collections.fs @@ -149,7 +149,7 @@ module Collections = let _12: WrappedListI<_> = seq [1;2] |> ofSeq #if TEST_TRACE - CollectionAssert.AreEqual (["OfSeq, Default2-#Add"; "OfSeq, Default2-#Add"; "OfSeq, Default2-#Add"; "OfSeq, Default4-seq<'t>"], Traces.get()) + CollectionAssert.AreEqual (["OfSeq, Default2-#Add"; "OfSeq, Default2-#Add"; "OfSeq, Default2-#Add"; "OfSeq, Return+Sum"], Traces.get()) #endif () From 88bd6b9b730058d7d448307ef2a54d15637b10e5 Mon Sep 17 00:00:00 2001 From: gusty <1261319+gusty@users.noreply.github.com> Date: Tue, 19 Dec 2023 10:10:46 +0100 Subject: [PATCH 07/15] + Overloads for Return --- src/FSharpPlus/Control/Monad.fs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/FSharpPlus/Control/Monad.fs b/src/FSharpPlus/Control/Monad.fs index 0b8418f61..792ee20bd 100644 --- a/src/FSharpPlus/Control/Monad.fs +++ b/src/FSharpPlus/Control/Monad.fs @@ -136,9 +136,13 @@ type Return = - static member Return (_: seq<'a> , _: Default2) = fun x -> Seq.singleton x : seq<'a> - static member Return (_: NonEmptySeq<'a>, _: Default2) = fun x -> NonEmptySeq.singleton x : NonEmptySeq<'a> - static member Return (_: IEnumerator<'a>, _: Default2) = fun x -> Enumerator.upto None (fun _ -> x) : IEnumerator<'a> + static member Return (_: seq<'a> , _: Default4) = fun x -> Seq.singleton x : seq<'a> + static member Return (_: NonEmptySeq<'a>, _: Default3) = fun x -> NonEmptySeq.singleton x : NonEmptySeq<'a> + static member Return (_: IEnumerator<'a>, _: Default3) = fun x -> Enumerator.upto None (fun _ -> x) : IEnumerator<'a> + static member Return (_: IDictionary<'k,'t> , _: Default2) = fun x -> Dict.initInfinite x : IDictionary<'k,'t> + #if (!FABLE_COMPILER_3) // TODO Dummy overload for now + static member Return (_: IReadOnlyDictionary<'k,'t>, _: Default3) = fun x -> readOnlyDict [Unchecked.defaultof<'k>, x] : IReadOnlyDictionary<'k,'t> + #endif static member inline Return (_: 'R , _: Default1) = fun (x: 'T) -> Return.InvokeOnInstance x : 'R static member Return (_: Lazy<'a> , _: Return ) = fun x -> Lazy<_>.CreateFromValue x : Lazy<'a> #if !FABLE_COMPILER @@ -157,7 +161,6 @@ type Return = static member Return (_: 'a Async , _: Return ) = fun (x: 'a) -> async.Return x static member Return (_: Result<'a,'e> , _: Return ) = fun x -> Ok x : Result<'a,'e> static member Return (_: Choice<'a,'e> , _: Return ) = fun x -> Choice1Of2 x : Choice<'a,'e> - static member Return (_: IDictionary<'k,'t>, _: Return) = fun x -> Dict.initInfinite x : IDictionary<'k,'t> #if !FABLE_COMPILER static member Return (_: Expr<'a> , _: Return ) = fun x -> Expr.Cast<'a> (Expr.Value (x: 'a)) From 33fdc1e513b546a6abd7be1de2a2e0ce706a36d4 Mon Sep 17 00:00:00 2001 From: gusty <1261319+gusty@users.noreply.github.com> Date: Wed, 4 Jun 2025 09:52:21 +0200 Subject: [PATCH 08/15] Allow unioning them --- src/FSharpPlus/Extensions/Dict.fs | 165 ++++++++++++++++++++++-------- 1 file changed, 120 insertions(+), 45 deletions(-) diff --git a/src/FSharpPlus/Extensions/Dict.fs b/src/FSharpPlus/Extensions/Dict.fs index 7a620da55..8fbfb41f4 100644 --- a/src/FSharpPlus/Extensions/Dict.fs +++ b/src/FSharpPlus/Extensions/Dict.fs @@ -129,13 +129,128 @@ module Dict = dct2.Add (k, vy) dct1 :> IDictionary<'Key, 'T1>, dct2 :> IDictionary<'Key, 'T2> + + /// Creates a conceptually infinite dictionay containing the same value for all possible keys. + /// The value for all possible keys. + let initInfinite<'TKey,'TValue> (source: 'TValue) : IDictionary<'TKey,'TValue> = + + let icollection value = + { + new ICollection<'t> with + member __.Contains (item: 't) = obj.ReferenceEquals (item, value) + member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () :> System.Collections.IEnumerator + member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () : IEnumerator<'t> + member __.IsReadOnly = true + + member __.Add (_item: 't) : unit = raise (System.NotImplementedException()) + member __.Clear () : unit = raise (System.NotImplementedException()) + member __.CopyTo (_array: 't [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) + member __.Count : int = -1 + member __.Remove (_item: 't): bool = raise (System.NotImplementedException()) + } + + { + new IDictionary<'TKey,'TValue> with + member __.TryGetValue (_key: 'TKey, value: byref<'TValue>) = value <- source; true + member __.Count = -1 + member __.ContainsKey (_key: 'TKey) = true + member __.Contains (item: KeyValuePair<'TKey,'TValue>) = obj.ReferenceEquals (item.Value, source) + member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : System.Collections.IEnumerator + member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : IEnumerator> + member __.IsReadOnly = true + member __.Values = icollection source + member __.Item + with get (_key: 'TKey) : 'TValue = source + and set (_key: 'TKey) (_: 'TValue) : unit = raise (System.NotImplementedException()) + + member __.Add (_key: 'TKey, _value: 'TValue) : unit = raise (System.NotImplementedException()) + member __.Add (_item: KeyValuePair<'TKey,'TValue>) : unit = raise (System.NotImplementedException()) + member __.Clear () : unit = raise (System.NotImplementedException()) + member __.CopyTo (_arr: KeyValuePair<'TKey,'TValue> [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) + member __.Keys : ICollection<'TKey> = raise (System.NotImplementedException()) + member __.Remove (_key: 'TKey) : bool = raise (System.NotImplementedException()) + member __.Remove (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException()) + } + + let initHybrid<'TKey,'TValue> (konst: 'TValue) (source: IDictionary<'TKey,'TValue>) : IDictionary<'TKey,'TValue> = + + let icollection (konst: 'TValue) (source: IDictionary<'TKey,'TValue>) = + { + new ICollection<'t> with + member __.Contains (item: 't) = source.Values.Contains item || obj.ReferenceEquals (item, konst) + member __.GetEnumerator () = (seq { yield! source.Values; yield! (Seq.initInfinite (fun _ -> konst))}).GetEnumerator () :> System.Collections.IEnumerator + member __.GetEnumerator () = (seq { yield! source.Values; yield! (Seq.initInfinite (fun _ -> konst))}).GetEnumerator () : IEnumerator<'t> + member __.IsReadOnly = true + + member __.Add (_item: 't) : unit = raise (System.NotImplementedException()) + member __.Clear () : unit = raise (System.NotImplementedException()) + member __.CopyTo (_array: 't [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) + member __.Count : int = -source.Count-1 + member __.Remove (_item: 't): bool = raise (System.NotImplementedException()) + } + { + new IDictionary<'TKey,'TValue> with + member __.TryGetValue (key: 'TKey, value: byref<'TValue>) = + match source.TryGetValue key with + | true, v -> value <- v + | _ -> value <- konst + true + member __.Count = -source.Count-1 + member __.ContainsKey (_key: 'TKey) = true + member __.Contains (item: KeyValuePair<'TKey,'TValue>) = + match source.TryGetValue item.Key with + | true, v -> obj.ReferenceEquals (item.Value, v) + | _ -> obj.ReferenceEquals (item.Value, konst) + member __.GetEnumerator () = source.GetEnumerator () : System.Collections.IEnumerator + member __.GetEnumerator () = source.GetEnumerator () : IEnumerator> + member __.IsReadOnly = true + member __.Values = icollection konst source + member __.Item + with get (key: 'TKey) : 'TValue = match source.TryGetValue key with (true, v) -> v | _ -> konst + and set (_key: 'TKey) (_: 'TValue) : unit = raise (System.NotImplementedException()) + + member __.Add (_key: 'TKey, _value: 'TValue) : unit = raise (System.NotImplementedException()) + member __.Add (_item: KeyValuePair<'TKey,'TValue>) : unit = raise (System.NotImplementedException()) + member __.Clear () : unit = raise (System.NotImplementedException()) + member __.CopyTo (_arr: KeyValuePair<'TKey,'TValue> [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) + member __.Keys : ICollection<'TKey> = raise (System.NotImplementedException()) + member __.Remove (_key: 'TKey) : bool = raise (System.NotImplementedException()) + member __.Remove (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException()) + } + /// Returns the union of two dictionaries, using the combiner function for duplicate keys. let unionWith combiner (source1: IDictionary<'Key, 'Value>) (source2: IDictionary<'Key, 'Value>) = - let d = Dictionary<'Key,'Value> () - let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt combiner - for KeyValue(k, v ) in source1 do d.[k] <- v - for KeyValue(k, v') in source2 do d.[k] <- match d.TryGetValue k with true, v -> f.Invoke (v, v') | _ -> v' - d :> IDictionary<'Key,'Value> + let combine () = + let d = Dictionary<'Key,'Value> () + let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt combiner + for KeyValue(k, v ) in source1 do d.[k] <- v + for KeyValue(k, v') in source2 do d.[k] <- match d.TryGetValue k with true, v -> f.Invoke (v, v') | _ -> v' + d :> IDictionary<'Key,'Value> + let combineWithKonst source konst = + let d = Dictionary<'Key,'Value> () + for KeyValue(k, v) in source do d.[k] <- combiner v konst + d :> IDictionary<'Key,'Value> + let combineKonstWith konst source = + let d = Dictionary<'Key,'Value> () + for KeyValue(k, v) in source do d.[k] <- combiner konst v + d :> IDictionary<'Key,'Value> + match source1.Count, source2.Count with + | -1, -1 -> + initInfinite ( + combiner + (source1[Unchecked.defaultof<'Key>]) + (source2[Unchecked.defaultof<'Key>])) + | -1, 0 -> source1 + | 0, -1 -> source2 + | -1, x when x < -1 -> initHybrid (combiner (source1[Unchecked.defaultof<'Key>]) (source2[Unchecked.defaultof<'Key>])) (combineKonstWith (source1[Unchecked.defaultof<'Key>]) source2) + | x, -1 when x < -1 -> initHybrid (combiner (source1[Unchecked.defaultof<'Key>]) (source2[Unchecked.defaultof<'Key>])) (combineWithKonst source1 (source2[Unchecked.defaultof<'Key>])) + // Note: this is horrible and Unchecked.defaultof<'Key> is 0 for int keys, so it might not return the default value. + // All this hints that a specific (named) class is needed, something like DictWithDefaultValue<'Key, 'Value> then type tests can be added before reading the defaultValue property. + | x, y when x < -1 && y < -1 -> initHybrid (combiner (source1[Unchecked.defaultof<'Key>]) (source2[Unchecked.defaultof<'Key>])) (combine()) + | -1, _ -> combineKonstWith (source1[Unchecked.defaultof<'Key>]) source2 |> initHybrid (source1[Unchecked.defaultof<'Key>]) + | _, -1 -> combineWithKonst source1 (source2[Unchecked.defaultof<'Key>]) |> initHybrid (source2[Unchecked.defaultof<'Key>]) + | _, _ -> combine() + #if !FABLE_COMPILER ///Returns the union of two maps, preferring values from the first in case of duplicate keys. @@ -179,44 +294,4 @@ module Dict = | None -> () dct :> IDictionary<'Key, 'U> - /// Creates a conceptually infinite dictionay containing the same value for all possible keys. - /// The value for all possible keys. - let initInfinite<'TKey,'TValue> (source: 'TValue) : IDictionary<'TKey,'TValue> = - - let icollection value = - { - new ICollection<'t> with - member __.Contains (item: 't) = obj.ReferenceEquals (item, value) - member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () :> System.Collections.IEnumerator - member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () : IEnumerator<'t> - member __.IsReadOnly = true - - member __.Add (_item: 't) : unit = raise (System.NotImplementedException()) - member __.Clear () : unit = raise (System.NotImplementedException()) - member __.CopyTo (_array: 't [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) - member __.Count : int = raise (System.NotImplementedException()) - member __.Remove (_item: 't): bool = raise (System.NotImplementedException()) - } - - { - new IDictionary<'TKey,'TValue> with - member __.TryGetValue (_key: 'TKey, value: byref<'TValue>) = value <- source; true - member __.Count = System.Int32.MaxValue - member __.ContainsKey (_key: 'TKey) = true - member __.Contains (item: KeyValuePair<'TKey,'TValue>) = obj.ReferenceEquals (item.Value, source) - member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : System.Collections.IEnumerator - member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : IEnumerator> - member __.IsReadOnly = true - member __.Values = icollection source - member __.Item - with get (_key: 'TKey) : 'TValue = source - and set (_key: 'TKey) (_: 'TValue) : unit = raise (System.NotImplementedException()) - member __.Add (_key: 'TKey, _value: 'TValue) : unit = raise (System.NotImplementedException()) - member __.Add (_item: KeyValuePair<'TKey,'TValue>) : unit = raise (System.NotImplementedException()) - member __.Clear () : unit = raise (System.NotImplementedException()) - member __.CopyTo (_arr: KeyValuePair<'TKey,'TValue> [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) - member __.Keys : ICollection<'TKey> = raise (System.NotImplementedException()) - member __.Remove (_key: 'TKey) : bool = raise (System.NotImplementedException()) - member __.Remove (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException()) - } From abf8fb314706a211d97a9dab81fb37bf214c6f3b Mon Sep 17 00:00:00 2001 From: Gus <1261319+gusty@users.noreply.github.com> Date: Wed, 11 Jun 2025 07:23:38 +0200 Subject: [PATCH 09/15] Move code up --- src/FSharpPlus/Extensions/Dict.fs | 177 +++++++++++++++--------------- 1 file changed, 88 insertions(+), 89 deletions(-) diff --git a/src/FSharpPlus/Extensions/Dict.fs b/src/FSharpPlus/Extensions/Dict.fs index 8fbfb41f4..77f49c475 100644 --- a/src/FSharpPlus/Extensions/Dict.fs +++ b/src/FSharpPlus/Extensions/Dict.fs @@ -6,6 +6,94 @@ module Dict = open System.Collections.Generic open System.Collections.ObjectModel + /// Creates a conceptually infinite dictionay containing the same value for all possible keys. + /// The value for all possible keys. + let initInfinite<'TKey,'TValue> (source: 'TValue) : IDictionary<'TKey,'TValue> = + + let icollection value = + { + new ICollection<'t> with + member __.Contains (item: 't) = obj.ReferenceEquals (item, value) + member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () :> System.Collections.IEnumerator + member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () : IEnumerator<'t> + member __.IsReadOnly = true + + member __.Add (_item: 't) : unit = raise (System.NotImplementedException()) + member __.Clear () : unit = raise (System.NotImplementedException()) + member __.CopyTo (_array: 't [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) + member __.Count : int = -1 + member __.Remove (_item: 't): bool = raise (System.NotImplementedException()) + } + + { + new IDictionary<'TKey,'TValue> with + member __.TryGetValue (_key: 'TKey, value: byref<'TValue>) = value <- source; true + member __.Count = -1 + member __.ContainsKey (_key: 'TKey) = true + member __.Contains (item: KeyValuePair<'TKey,'TValue>) = obj.ReferenceEquals (item.Value, source) + member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : System.Collections.IEnumerator + member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : IEnumerator> + member __.IsReadOnly = true + member __.Values = icollection source + member __.Item + with get (_key: 'TKey) : 'TValue = source + and set (_key: 'TKey) (_: 'TValue) : unit = raise (System.NotImplementedException()) + + member __.Add (_key: 'TKey, _value: 'TValue) : unit = raise (System.NotImplementedException()) + member __.Add (_item: KeyValuePair<'TKey,'TValue>) : unit = raise (System.NotImplementedException()) + member __.Clear () : unit = raise (System.NotImplementedException()) + member __.CopyTo (_arr: KeyValuePair<'TKey,'TValue> [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) + member __.Keys : ICollection<'TKey> = raise (System.NotImplementedException()) + member __.Remove (_key: 'TKey) : bool = raise (System.NotImplementedException()) + member __.Remove (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException()) + } + + let initHybrid<'TKey,'TValue> (konst: 'TValue) (source: IDictionary<'TKey,'TValue>) : IDictionary<'TKey,'TValue> = + + let icollection (konst: 'TValue) (source: IDictionary<'TKey,'TValue>) = + { + new ICollection<'t> with + member __.Contains (item: 't) = source.Values.Contains item || obj.ReferenceEquals (item, konst) + member __.GetEnumerator () = (seq { yield! source.Values; yield! (Seq.initInfinite (fun _ -> konst))}).GetEnumerator () :> System.Collections.IEnumerator + member __.GetEnumerator () = (seq { yield! source.Values; yield! (Seq.initInfinite (fun _ -> konst))}).GetEnumerator () : IEnumerator<'t> + member __.IsReadOnly = true + + member __.Add (_item: 't) : unit = raise (System.NotImplementedException()) + member __.Clear () : unit = raise (System.NotImplementedException()) + member __.CopyTo (_array: 't [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) + member __.Count : int = -source.Count-1 + member __.Remove (_item: 't): bool = raise (System.NotImplementedException()) + } + { + new IDictionary<'TKey,'TValue> with + member __.TryGetValue (key: 'TKey, value: byref<'TValue>) = + match source.TryGetValue key with + | true, v -> value <- v + | _ -> value <- konst + true + member __.Count = -source.Count-1 + member __.ContainsKey (_key: 'TKey) = true + member __.Contains (item: KeyValuePair<'TKey,'TValue>) = + match source.TryGetValue item.Key with + | true, v -> obj.ReferenceEquals (item.Value, v) + | _ -> obj.ReferenceEquals (item.Value, konst) + member __.GetEnumerator () = source.GetEnumerator () : System.Collections.IEnumerator + member __.GetEnumerator () = source.GetEnumerator () : IEnumerator> + member __.IsReadOnly = true + member __.Values = icollection konst source + member __.Item + with get (key: 'TKey) : 'TValue = match source.TryGetValue key with (true, v) -> v | _ -> konst + and set (_key: 'TKey) (_: 'TValue) : unit = raise (System.NotImplementedException()) + + member __.Add (_key: 'TKey, _value: 'TValue) : unit = raise (System.NotImplementedException()) + member __.Add (_item: KeyValuePair<'TKey,'TValue>) : unit = raise (System.NotImplementedException()) + member __.Clear () : unit = raise (System.NotImplementedException()) + member __.CopyTo (_arr: KeyValuePair<'TKey,'TValue> [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) + member __.Keys : ICollection<'TKey> = raise (System.NotImplementedException()) + member __.Remove (_key: 'TKey) : bool = raise (System.NotImplementedException()) + member __.Remove (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException()) + } + #if !FABLE_COMPILER open System.Linq @@ -129,95 +217,6 @@ module Dict = dct2.Add (k, vy) dct1 :> IDictionary<'Key, 'T1>, dct2 :> IDictionary<'Key, 'T2> - - /// Creates a conceptually infinite dictionay containing the same value for all possible keys. - /// The value for all possible keys. - let initInfinite<'TKey,'TValue> (source: 'TValue) : IDictionary<'TKey,'TValue> = - - let icollection value = - { - new ICollection<'t> with - member __.Contains (item: 't) = obj.ReferenceEquals (item, value) - member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () :> System.Collections.IEnumerator - member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () : IEnumerator<'t> - member __.IsReadOnly = true - - member __.Add (_item: 't) : unit = raise (System.NotImplementedException()) - member __.Clear () : unit = raise (System.NotImplementedException()) - member __.CopyTo (_array: 't [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) - member __.Count : int = -1 - member __.Remove (_item: 't): bool = raise (System.NotImplementedException()) - } - - { - new IDictionary<'TKey,'TValue> with - member __.TryGetValue (_key: 'TKey, value: byref<'TValue>) = value <- source; true - member __.Count = -1 - member __.ContainsKey (_key: 'TKey) = true - member __.Contains (item: KeyValuePair<'TKey,'TValue>) = obj.ReferenceEquals (item.Value, source) - member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : System.Collections.IEnumerator - member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : IEnumerator> - member __.IsReadOnly = true - member __.Values = icollection source - member __.Item - with get (_key: 'TKey) : 'TValue = source - and set (_key: 'TKey) (_: 'TValue) : unit = raise (System.NotImplementedException()) - - member __.Add (_key: 'TKey, _value: 'TValue) : unit = raise (System.NotImplementedException()) - member __.Add (_item: KeyValuePair<'TKey,'TValue>) : unit = raise (System.NotImplementedException()) - member __.Clear () : unit = raise (System.NotImplementedException()) - member __.CopyTo (_arr: KeyValuePair<'TKey,'TValue> [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) - member __.Keys : ICollection<'TKey> = raise (System.NotImplementedException()) - member __.Remove (_key: 'TKey) : bool = raise (System.NotImplementedException()) - member __.Remove (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException()) - } - - let initHybrid<'TKey,'TValue> (konst: 'TValue) (source: IDictionary<'TKey,'TValue>) : IDictionary<'TKey,'TValue> = - - let icollection (konst: 'TValue) (source: IDictionary<'TKey,'TValue>) = - { - new ICollection<'t> with - member __.Contains (item: 't) = source.Values.Contains item || obj.ReferenceEquals (item, konst) - member __.GetEnumerator () = (seq { yield! source.Values; yield! (Seq.initInfinite (fun _ -> konst))}).GetEnumerator () :> System.Collections.IEnumerator - member __.GetEnumerator () = (seq { yield! source.Values; yield! (Seq.initInfinite (fun _ -> konst))}).GetEnumerator () : IEnumerator<'t> - member __.IsReadOnly = true - - member __.Add (_item: 't) : unit = raise (System.NotImplementedException()) - member __.Clear () : unit = raise (System.NotImplementedException()) - member __.CopyTo (_array: 't [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) - member __.Count : int = -source.Count-1 - member __.Remove (_item: 't): bool = raise (System.NotImplementedException()) - } - { - new IDictionary<'TKey,'TValue> with - member __.TryGetValue (key: 'TKey, value: byref<'TValue>) = - match source.TryGetValue key with - | true, v -> value <- v - | _ -> value <- konst - true - member __.Count = -source.Count-1 - member __.ContainsKey (_key: 'TKey) = true - member __.Contains (item: KeyValuePair<'TKey,'TValue>) = - match source.TryGetValue item.Key with - | true, v -> obj.ReferenceEquals (item.Value, v) - | _ -> obj.ReferenceEquals (item.Value, konst) - member __.GetEnumerator () = source.GetEnumerator () : System.Collections.IEnumerator - member __.GetEnumerator () = source.GetEnumerator () : IEnumerator> - member __.IsReadOnly = true - member __.Values = icollection konst source - member __.Item - with get (key: 'TKey) : 'TValue = match source.TryGetValue key with (true, v) -> v | _ -> konst - and set (_key: 'TKey) (_: 'TValue) : unit = raise (System.NotImplementedException()) - - member __.Add (_key: 'TKey, _value: 'TValue) : unit = raise (System.NotImplementedException()) - member __.Add (_item: KeyValuePair<'TKey,'TValue>) : unit = raise (System.NotImplementedException()) - member __.Clear () : unit = raise (System.NotImplementedException()) - member __.CopyTo (_arr: KeyValuePair<'TKey,'TValue> [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) - member __.Keys : ICollection<'TKey> = raise (System.NotImplementedException()) - member __.Remove (_key: 'TKey) : bool = raise (System.NotImplementedException()) - member __.Remove (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException()) - } - /// Returns the union of two dictionaries, using the combiner function for duplicate keys. let unionWith combiner (source1: IDictionary<'Key, 'Value>) (source2: IDictionary<'Key, 'Value>) = let combine () = From 9d0a8d85c30ea89bf20e41d5d19bed77a58b7c7d Mon Sep 17 00:00:00 2001 From: gusty <1261319+gusty@users.noreply.github.com> Date: Thu, 12 Jun 2025 19:49:37 +0200 Subject: [PATCH 10/15] Use a (named) type --- src/FSharpPlus/Extensions/Dict.fs | 182 ++++++++++++------------------ 1 file changed, 74 insertions(+), 108 deletions(-) diff --git a/src/FSharpPlus/Extensions/Dict.fs b/src/FSharpPlus/Extensions/Dict.fs index 77f49c475..0560f7578 100644 --- a/src/FSharpPlus/Extensions/Dict.fs +++ b/src/FSharpPlus/Extensions/Dict.fs @@ -1,98 +1,76 @@ namespace FSharpPlus -/// Additional operations on IDictionary<'Key, 'Value> -[] -module Dict = +[] +module Auto = + open System + open System.Collections open System.Collections.Generic - open System.Collections.ObjectModel - - /// Creates a conceptually infinite dictionay containing the same value for all possible keys. - /// The value for all possible keys. - let initInfinite<'TKey,'TValue> (source: 'TValue) : IDictionary<'TKey,'TValue> = - - let icollection value = - { - new ICollection<'t> with - member __.Contains (item: 't) = obj.ReferenceEquals (item, value) - member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () :> System.Collections.IEnumerator - member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () : IEnumerator<'t> - member __.IsReadOnly = true - - member __.Add (_item: 't) : unit = raise (System.NotImplementedException()) - member __.Clear () : unit = raise (System.NotImplementedException()) - member __.CopyTo (_array: 't [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) - member __.Count : int = -1 - member __.Remove (_item: 't): bool = raise (System.NotImplementedException()) - } + let icollection (konst: 'TValue) (source: IDictionary<'TKey,'TValue>) = { - new IDictionary<'TKey,'TValue> with - member __.TryGetValue (_key: 'TKey, value: byref<'TValue>) = value <- source; true - member __.Count = -1 - member __.ContainsKey (_key: 'TKey) = true - member __.Contains (item: KeyValuePair<'TKey,'TValue>) = obj.ReferenceEquals (item.Value, source) - member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : System.Collections.IEnumerator - member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : IEnumerator> - member __.IsReadOnly = true - member __.Values = icollection source - member __.Item - with get (_key: 'TKey) : 'TValue = source - and set (_key: 'TKey) (_: 'TValue) : unit = raise (System.NotImplementedException()) + new ICollection<'TValue> with + member _.Contains item = source.Values.Contains item || obj.ReferenceEquals (item, konst) + member _.GetEnumerator () = (seq { yield! source.Values; yield! (Seq.initInfinite (fun _ -> konst))}).GetEnumerator () :> System.Collections.IEnumerator + member _.GetEnumerator () = (seq { yield! source.Values; yield! (Seq.initInfinite (fun _ -> konst))}).GetEnumerator () : IEnumerator<'TValue> + member _.IsReadOnly = true + member _.Add (_item: 'TValue) : unit = raise (NotImplementedException ()) + member _.Clear () : unit = raise (NotImplementedException ()) + member _.CopyTo (_array: 'TValue [], _arrayIndex: int) : unit = raise (NotImplementedException ()) + member _.Count : int = source.Count + member _.Remove (_item: 'TValue): bool = raise (NotImplementedException ()) + } - member __.Add (_key: 'TKey, _value: 'TValue) : unit = raise (System.NotImplementedException()) - member __.Add (_item: KeyValuePair<'TKey,'TValue>) : unit = raise (System.NotImplementedException()) - member __.Clear () : unit = raise (System.NotImplementedException()) - member __.CopyTo (_arr: KeyValuePair<'TKey,'TValue> [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) - member __.Keys : ICollection<'TKey> = raise (System.NotImplementedException()) - member __.Remove (_key: 'TKey) : bool = raise (System.NotImplementedException()) - member __.Remove (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException()) - } + type DefaultableDict<'TKey, 'TValue> (konst: 'TValue, source: IDictionary<'TKey,'TValue>) = - let initHybrid<'TKey,'TValue> (konst: 'TValue) (source: IDictionary<'TKey,'TValue>) : IDictionary<'TKey,'TValue> = + interface IDictionary<'TKey, 'TValue> with + member _.TryGetValue (key: 'TKey, value: byref<'TValue>) = + match source.TryGetValue key with + | true, v -> value <- v + | _ -> value <- konst + true + member _.Count = source.Count + // if typeof<'TKey>.IsValueType then + // if typeof<'TKey> = typeof then 2 + // else + // let s = sizeof<'TKey> + // if s < 4 then pown 2 (sizeof<'TKey> * 8) + // else -1 // infinity + // elif typeof<'TKey> = typeof then 1 + // else -1 + member _.ContainsKey (_key: 'TKey) = true + member _.Contains (item: KeyValuePair<'TKey,'TValue>) = + match source.TryGetValue item.Key with + | true, v -> obj.ReferenceEquals (item.Value, v) + | _ -> obj.ReferenceEquals (item.Value, konst) + member _.GetEnumerator () = source.GetEnumerator () : System.Collections.IEnumerator + member _.GetEnumerator () = source.GetEnumerator () : IEnumerator> + member _.IsReadOnly = true + member _.Values = icollection konst source + member _.Item + with get (key: 'TKey) : 'TValue = match source.TryGetValue key with (true, v) -> v | _ -> konst + and set (_key: 'TKey) (_: 'TValue) : unit = raise (System.NotImplementedException()) - let icollection (konst: 'TValue) (source: IDictionary<'TKey,'TValue>) = - { - new ICollection<'t> with - member __.Contains (item: 't) = source.Values.Contains item || obj.ReferenceEquals (item, konst) - member __.GetEnumerator () = (seq { yield! source.Values; yield! (Seq.initInfinite (fun _ -> konst))}).GetEnumerator () :> System.Collections.IEnumerator - member __.GetEnumerator () = (seq { yield! source.Values; yield! (Seq.initInfinite (fun _ -> konst))}).GetEnumerator () : IEnumerator<'t> - member __.IsReadOnly = true + member _.Add (_key: 'TKey, _value: 'TValue) : unit = raise (NotImplementedException ()) + member _.Add (_item: KeyValuePair<'TKey,'TValue>) : unit = raise (NotImplementedException ()) + member _.Clear () : unit = raise (NotImplementedException ()) + member _.CopyTo (_arr: KeyValuePair<'TKey,'TValue> [], _arrayIndex: int) : unit = raise (NotImplementedException ()) + member _.Keys : ICollection<'TKey> = raise (NotImplementedException ()) + member _.Remove (_key: 'TKey) : bool = raise (NotImplementedException ()) + member _.Remove (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (NotImplementedException ()) + + member _.DefaultValue = konst - member __.Add (_item: 't) : unit = raise (System.NotImplementedException()) - member __.Clear () : unit = raise (System.NotImplementedException()) - member __.CopyTo (_array: 't [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) - member __.Count : int = -source.Count-1 - member __.Remove (_item: 't): bool = raise (System.NotImplementedException()) - } - { - new IDictionary<'TKey,'TValue> with - member __.TryGetValue (key: 'TKey, value: byref<'TValue>) = - match source.TryGetValue key with - | true, v -> value <- v - | _ -> value <- konst - true - member __.Count = -source.Count-1 - member __.ContainsKey (_key: 'TKey) = true - member __.Contains (item: KeyValuePair<'TKey,'TValue>) = - match source.TryGetValue item.Key with - | true, v -> obj.ReferenceEquals (item.Value, v) - | _ -> obj.ReferenceEquals (item.Value, konst) - member __.GetEnumerator () = source.GetEnumerator () : System.Collections.IEnumerator - member __.GetEnumerator () = source.GetEnumerator () : IEnumerator> - member __.IsReadOnly = true - member __.Values = icollection konst source - member __.Item - with get (key: 'TKey) : 'TValue = match source.TryGetValue key with (true, v) -> v | _ -> konst - and set (_key: 'TKey) (_: 'TValue) : unit = raise (System.NotImplementedException()) +/// Additional operations on IDictionary<'Key, 'Value> +[] +module Dict = + open System.Collections.Generic + open System.Collections.ObjectModel + open Auto - member __.Add (_key: 'TKey, _value: 'TValue) : unit = raise (System.NotImplementedException()) - member __.Add (_item: KeyValuePair<'TKey,'TValue>) : unit = raise (System.NotImplementedException()) - member __.Clear () : unit = raise (System.NotImplementedException()) - member __.CopyTo (_arr: KeyValuePair<'TKey,'TValue> [], _arrayIndex: int) : unit = raise (System.NotImplementedException()) - member __.Keys : ICollection<'TKey> = raise (System.NotImplementedException()) - member __.Remove (_key: 'TKey) : bool = raise (System.NotImplementedException()) - member __.Remove (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException()) - } + /// Creates a conceptually infinite dictionay containing the same value for all possible keys. + /// The value for all possible keys. + let initInfinite<'TKey,'TValue when 'TKey : equality> (source: 'TValue) : IDictionary<'TKey,'TValue> = new DefaultableDict<'TKey,'TValue>(source, Dictionary<'TKey,'TValue> ()) + let initHybrid<'TKey,'TValue> (konst: 'TValue) (source: IDictionary<'TKey,'TValue>) : IDictionary<'TKey,'TValue> = new DefaultableDict<'TKey,'TValue>(konst, source) #if !FABLE_COMPILER open System.Linq @@ -225,29 +203,17 @@ module Dict = for KeyValue(k, v ) in source1 do d.[k] <- v for KeyValue(k, v') in source2 do d.[k] <- match d.TryGetValue k with true, v -> f.Invoke (v, v') | _ -> v' d :> IDictionary<'Key,'Value> - let combineWithKonst source konst = - let d = Dictionary<'Key,'Value> () - for KeyValue(k, v) in source do d.[k] <- combiner v konst - d :> IDictionary<'Key,'Value> - let combineKonstWith konst source = - let d = Dictionary<'Key,'Value> () - for KeyValue(k, v) in source do d.[k] <- combiner konst v - d :> IDictionary<'Key,'Value> - match source1.Count, source2.Count with - | -1, -1 -> - initInfinite ( - combiner - (source1[Unchecked.defaultof<'Key>]) - (source2[Unchecked.defaultof<'Key>])) - | -1, 0 -> source1 - | 0, -1 -> source2 - | -1, x when x < -1 -> initHybrid (combiner (source1[Unchecked.defaultof<'Key>]) (source2[Unchecked.defaultof<'Key>])) (combineKonstWith (source1[Unchecked.defaultof<'Key>]) source2) - | x, -1 when x < -1 -> initHybrid (combiner (source1[Unchecked.defaultof<'Key>]) (source2[Unchecked.defaultof<'Key>])) (combineWithKonst source1 (source2[Unchecked.defaultof<'Key>])) - // Note: this is horrible and Unchecked.defaultof<'Key> is 0 for int keys, so it might not return the default value. - // All this hints that a specific (named) class is needed, something like DictWithDefaultValue<'Key, 'Value> then type tests can be added before reading the defaultValue property. - | x, y when x < -1 && y < -1 -> initHybrid (combiner (source1[Unchecked.defaultof<'Key>]) (source2[Unchecked.defaultof<'Key>])) (combine()) - | -1, _ -> combineKonstWith (source1[Unchecked.defaultof<'Key>]) source2 |> initHybrid (source1[Unchecked.defaultof<'Key>]) - | _, -1 -> combineWithKonst source1 (source2[Unchecked.defaultof<'Key>]) |> initHybrid (source2[Unchecked.defaultof<'Key>]) + // let combineWithKonst source konst = + // let d = Dictionary<'Key,'Value> () + // for KeyValue(k, v) in source do d.[k] <- combiner v konst + // d :> IDictionary<'Key,'Value> + // let combineKonstWith konst source = + // let d = Dictionary<'Key,'Value> () + // for KeyValue(k, v) in source do d.[k] <- combiner konst v + // d :> IDictionary<'Key,'Value> + match source1, source2 with + | (:? DefaultableDict<'Key,'Value> as s1), (:? DefaultableDict<'Key,'Value> as s2) -> initHybrid (combiner s1.DefaultValue s2.DefaultValue) (combine()) + | s, empty | empty, s when empty.Count = 0 -> s | _, _ -> combine() From ebc40ef5c59824220e4db8830ac5e17584399165 Mon Sep 17 00:00:00 2001 From: Gus <1261319+gusty@users.noreply.github.com> Date: Fri, 13 Jun 2025 20:12:56 +0200 Subject: [PATCH 11/15] typo Co-authored-by: Hadrian Tang --- src/FSharpPlus/Extensions/Dict.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpPlus/Extensions/Dict.fs b/src/FSharpPlus/Extensions/Dict.fs index 0560f7578..5ef689ebc 100644 --- a/src/FSharpPlus/Extensions/Dict.fs +++ b/src/FSharpPlus/Extensions/Dict.fs @@ -67,7 +67,7 @@ module Dict = open System.Collections.ObjectModel open Auto - /// Creates a conceptually infinite dictionay containing the same value for all possible keys. + /// Creates a conceptually infinite dictionary containing the same value for all possible keys. /// The value for all possible keys. let initInfinite<'TKey,'TValue when 'TKey : equality> (source: 'TValue) : IDictionary<'TKey,'TValue> = new DefaultableDict<'TKey,'TValue>(source, Dictionary<'TKey,'TValue> ()) let initHybrid<'TKey,'TValue> (konst: 'TValue) (source: IDictionary<'TKey,'TValue>) : IDictionary<'TKey,'TValue> = new DefaultableDict<'TKey,'TValue>(konst, source) From 175713b53aa36b7e8f9dc18be8617f0c4a6c45ff Mon Sep 17 00:00:00 2001 From: gusty <1261319+gusty@users.noreply.github.com> Date: Sat, 14 Jun 2025 11:43:26 +0200 Subject: [PATCH 12/15] Remove spaces --- src/FSharpPlus/Extensions/Dict.fs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/FSharpPlus/Extensions/Dict.fs b/src/FSharpPlus/Extensions/Dict.fs index 5ef689ebc..a2d935c25 100644 --- a/src/FSharpPlus/Extensions/Dict.fs +++ b/src/FSharpPlus/Extensions/Dict.fs @@ -258,5 +258,3 @@ module Dict = | Some v -> dct.Add (k, v) | None -> () dct :> IDictionary<'Key, 'U> - - From d721e0e7535a5af74f597d520e769ab8537f4763 Mon Sep 17 00:00:00 2001 From: gusty <1261319+gusty@users.noreply.github.com> Date: Sat, 14 Jun 2025 21:08:50 +0200 Subject: [PATCH 13/15] Use map2 --- src/FSharpPlus/Control/Applicative.fs | 7 +--- src/FSharpPlus/Control/ZipApplicative.fs | 8 +++- src/FSharpPlus/Extensions/Dict.fs | 47 +++++++++++++++--------- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/FSharpPlus/Control/Applicative.fs b/src/FSharpPlus/Control/Applicative.fs index 7949d0740..06ee5bcc9 100644 --- a/src/FSharpPlus/Control/Applicative.fs +++ b/src/FSharpPlus/Control/Applicative.fs @@ -57,12 +57,7 @@ type Apply = dct static member ``<*>`` (struct (f: IDictionary<'Key,_>, x: IDictionary<'Key,'T>) , _output: IDictionary<'Key,'U> , []_mthd: Apply) : IDictionary<'Key,'U> = - let dct = Dictionary () - for KeyValue(k, vf) in f do - match x.TryGetValue k with - | true, vx -> dct.Add (k, vf vx) - | _ -> () - dct :> IDictionary<'Key,'U> + Dict.map2 id f x static member ``<*>`` (struct (f: IReadOnlyDictionary<'Key,_>, x: IReadOnlyDictionary<'Key,'T>) , _output: IReadOnlyDictionary<'Key,'U> , []_mthd: Apply) : IReadOnlyDictionary<'Key,'U> = let dct = Dictionary () diff --git a/src/FSharpPlus/Control/ZipApplicative.fs b/src/FSharpPlus/Control/ZipApplicative.fs index 53ccfe1f1..e5a0bb29f 100644 --- a/src/FSharpPlus/Control/ZipApplicative.fs +++ b/src/FSharpPlus/Control/ZipApplicative.fs @@ -29,8 +29,12 @@ type Pure = let inline call (mthd: ^M, output: ^R) = ((^M or ^R) : (static member Pure : _*_ -> _) output, mthd) call (Unchecked.defaultof, Unchecked.defaultof<'``ZipApplicative<'T>``>) x - static member Pure (_: seq<'a> , _: Default2 ) = fun x -> Seq.initInfinite (fun _ -> x) : seq<'a> - static member Pure (_: IEnumerator<'a> , _: Default2 ) = fun x -> Enumerator.upto None (fun _ -> x) : IEnumerator<'a> + static member Pure (_: seq<'a> , _: Default4 ) = fun x -> Seq.initInfinite (fun _ -> x) : seq<'a> + static member Pure (_: IEnumerator<'a> , _: Default3 ) = fun x -> Enumerator.upto None (fun _ -> x) : IEnumerator<'a> + static member Pure (_: IDictionary<'k,'t>, _: Default2) = fun x -> Dict.initInfinite x: IDictionary<'k,'t> + #if (!FABLE_COMPILER_3) // TODO Dummy overload for now + static member Pure (_: IReadOnlyDictionary<'k,'t>, _: Default3) = fun x -> readOnlyDict [Unchecked.defaultof<'k>, x] : IReadOnlyDictionary<'k,'t> + #endif static member inline Pure (_: 'R , _: Default1 ) = fun (x: 'T) -> Pure.InvokeOnInstance x : 'R static member Pure (x: Lazy<'a> , _: Pure) = Return.Return (x, Unchecked.defaultof) : _ -> Lazy<'a> #if !FABLE_COMPILER diff --git a/src/FSharpPlus/Extensions/Dict.fs b/src/FSharpPlus/Extensions/Dict.fs index d69f6d7a9..d58ca57da 100644 --- a/src/FSharpPlus/Extensions/Dict.fs +++ b/src/FSharpPlus/Extensions/Dict.fs @@ -128,10 +128,18 @@ module Dict = /// /// The mapped dictionary. let map mapper (source: IDictionary<'Key, 'T>) = - let dct = Dictionary<'Key, 'U> () - for KeyValue(k, v) in source do - dct.Add (k, mapper v) - dct :> IDictionary<'Key, 'U> + match source with + | :? DefaultableDict<'Key, 'T> as s -> + let dct = DefaultableDict<'Key, 'U> (mapper s.DefaultValue, Dictionary<'Key, 'U> ()) + let dct = dct :> IDictionary<'Key, 'U> + for KeyValue(k, v) in source do + dct.Add (k, mapper v) + dct + | _ -> + let dct = Dictionary<'Key, 'U> () + for KeyValue(k, v) in source do + dct.Add (k, mapper v) + dct :> IDictionary<'Key, 'U> /// Creates a Dictionary value from a pair of Dictionaries, using a function to combine them. /// Keys that are not present on both dictionaries are dropped. @@ -141,13 +149,21 @@ module Dict = /// /// The combined dictionary. let map2 mapper (source1: IDictionary<'Key, 'T1>) (source2: IDictionary<'Key, 'T2>) = - let dct = Dictionary<'Key, 'U> () - let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt mapper - for KeyValue(k, vx) in source1 do - match tryGetValue k source2 with - | Some vy -> dct.Add (k, f.Invoke (vx, vy)) - | None -> () - dct :> IDictionary<'Key, 'U> + let map k1 k2 = + let dct = Dictionary<'Key, 'U> () + let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt mapper + for k in set source1.Keys + set source2.Keys do + match tryGetValue k source1, tryGetValue k source2, k1, k2 with + | Some vx, Some vy, _ , _ + | None , Some vy, Some vx, _ + | Some vx, None , _ , Some vy -> dct.Add (k, f.Invoke (vx, vy)) + | _ , _ , _ , _ -> () + dct :> IDictionary<'Key, 'U> + match source1, source2 with + | (:? DefaultableDict<'Key,'T1> as s1), (:? DefaultableDict<'Key,'T2> as s2) -> initHybrid (mapper s1.DefaultValue s2.DefaultValue) (map (Some s1.DefaultValue) (Some s2.DefaultValue)) + | (:? DefaultableDict<'Key,'T1> as s1), _ -> map (Some s1.DefaultValue) None + | _, (:? DefaultableDict<'Key,'T2> as s2) -> map None (Some s2.DefaultValue) + | _, _ -> map None None /// Combines values from three dictionaries using mapping function. /// Keys that are not present on every dictionary are dropped. @@ -185,13 +201,7 @@ module Dict = /// The second input dictionary. /// /// The tupled dictionary. - let zip (source1: IDictionary<'Key, 'T1>) (source2: IDictionary<'Key, 'T2>) = - let dct = Dictionary<'Key, 'T1 * 'T2> () - for KeyValue(k, vx) in source1 do - match tryGetValue k source2 with - | Some vy -> dct.Add (k, (vx, vy)) - | None -> () - dct :> IDictionary<'Key, 'T1 * 'T2> + let zip (source1: IDictionary<'Key, 'T1>) (source2: IDictionary<'Key, 'T2>) = map2 (fun x y -> (x, y)) source1 source2 /// Splits a dictionary with tuple pair values to two separate dictionaries. /// The source dictionary. @@ -223,6 +233,7 @@ module Dict = // d :> IDictionary<'Key,'Value> match source1, source2 with | (:? DefaultableDict<'Key,'Value> as s1), (:? DefaultableDict<'Key,'Value> as s2) -> initHybrid (combiner s1.DefaultValue s2.DefaultValue) (combine()) + | (:? DefaultableDict<'Key,'Value> as s), _ | _, (:? DefaultableDict<'Key,'Value> as s) -> initHybrid s.DefaultValue (combine()) | s, empty | empty, s when empty.Count = 0 -> s | _, _ -> combine() From 9431c757837cff9e6026db59b89d523885d0b4b9 Mon Sep 17 00:00:00 2001 From: gusty <1261319+gusty@users.noreply.github.com> Date: Sun, 15 Jun 2025 10:14:09 +0200 Subject: [PATCH 14/15] Don't pretend it's infinite, it's just defaultable --- src/FSharpPlus/Extensions/Dict.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FSharpPlus/Extensions/Dict.fs b/src/FSharpPlus/Extensions/Dict.fs index d58ca57da..a45f3a978 100644 --- a/src/FSharpPlus/Extensions/Dict.fs +++ b/src/FSharpPlus/Extensions/Dict.fs @@ -10,8 +10,8 @@ module Auto = { new ICollection<'TValue> with member _.Contains item = source.Values.Contains item || obj.ReferenceEquals (item, konst) - member _.GetEnumerator () = (seq { yield! source.Values; yield! (Seq.initInfinite (fun _ -> konst))}).GetEnumerator () :> System.Collections.IEnumerator - member _.GetEnumerator () = (seq { yield! source.Values; yield! (Seq.initInfinite (fun _ -> konst))}).GetEnumerator () : IEnumerator<'TValue> + member _.GetEnumerator () = source.Values.GetEnumerator () :> System.Collections.IEnumerator + member _.GetEnumerator () = source.Values.GetEnumerator () : IEnumerator<'TValue> member _.IsReadOnly = true member _.Add (_item: 'TValue) : unit = raise (NotImplementedException ()) member _.Clear () : unit = raise (NotImplementedException ()) From 221911c43b6a545b14aef221ec85fb93280d43ec Mon Sep 17 00:00:00 2001 From: gusty <1261319+gusty@users.noreply.github.com> Date: Sun, 15 Jun 2025 10:39:51 +0200 Subject: [PATCH 15/15] Simplify code --- src/FSharpPlus/Control/Monad.fs | 2 +- src/FSharpPlus/Control/ZipApplicative.fs | 2 +- src/FSharpPlus/Extensions/Dict.fs | 95 ++++++++++-------------- 3 files changed, 43 insertions(+), 56 deletions(-) diff --git a/src/FSharpPlus/Control/Monad.fs b/src/FSharpPlus/Control/Monad.fs index 79d24c89e..27bbefc87 100644 --- a/src/FSharpPlus/Control/Monad.fs +++ b/src/FSharpPlus/Control/Monad.fs @@ -165,7 +165,7 @@ type Return = static member Return (_: seq<'a> , _: Default4) = fun x -> Seq.singleton x : seq<'a> static member Return (_: IEnumerator<'a>, _: Default3) = fun x -> Enumerator.upto None (fun _ -> x) : IEnumerator<'a> - static member Return (_: IDictionary<'k,'t> , _: Default2) = fun x -> Dict.initInfinite x : IDictionary<'k,'t> + static member Return (_: IDictionary<'k,'t> , _: Default2) = fun x -> Dict.emptyWithDefault x : IDictionary<'k,'t> #if (!FABLE_COMPILER_3) // TODO Dummy overload for now static member Return (_: IReadOnlyDictionary<'k,'t>, _: Default3) = fun x -> readOnlyDict [Unchecked.defaultof<'k>, x] : IReadOnlyDictionary<'k,'t> #endif diff --git a/src/FSharpPlus/Control/ZipApplicative.fs b/src/FSharpPlus/Control/ZipApplicative.fs index e5a0bb29f..62a7ac583 100644 --- a/src/FSharpPlus/Control/ZipApplicative.fs +++ b/src/FSharpPlus/Control/ZipApplicative.fs @@ -31,7 +31,7 @@ type Pure = static member Pure (_: seq<'a> , _: Default4 ) = fun x -> Seq.initInfinite (fun _ -> x) : seq<'a> static member Pure (_: IEnumerator<'a> , _: Default3 ) = fun x -> Enumerator.upto None (fun _ -> x) : IEnumerator<'a> - static member Pure (_: IDictionary<'k,'t>, _: Default2) = fun x -> Dict.initInfinite x: IDictionary<'k,'t> + static member Pure (_: IDictionary<'k,'t>, _: Default2) = fun x -> Dict.emptyWithDefault x: IDictionary<'k,'t> #if (!FABLE_COMPILER_3) // TODO Dummy overload for now static member Pure (_: IReadOnlyDictionary<'k,'t>, _: Default3) = fun x -> readOnlyDict [Unchecked.defaultof<'k>, x] : IReadOnlyDictionary<'k,'t> #endif diff --git a/src/FSharpPlus/Extensions/Dict.fs b/src/FSharpPlus/Extensions/Dict.fs index a45f3a978..be9345493 100644 --- a/src/FSharpPlus/Extensions/Dict.fs +++ b/src/FSharpPlus/Extensions/Dict.fs @@ -29,14 +29,6 @@ module Auto = | _ -> value <- konst true member _.Count = source.Count - // if typeof<'TKey>.IsValueType then - // if typeof<'TKey> = typeof then 2 - // else - // let s = sizeof<'TKey> - // if s < 4 then pown 2 (sizeof<'TKey> * 8) - // else -1 // infinity - // elif typeof<'TKey> = typeof then 1 - // else -1 member _.ContainsKey (_key: 'TKey) = true member _.Contains (item: KeyValuePair<'TKey,'TValue>) = match source.TryGetValue item.Key with @@ -67,10 +59,15 @@ module Dict = open System.Collections.ObjectModel open Auto - /// Creates a conceptually infinite dictionary containing the same value for all possible keys. - /// The value for all possible keys. - let initInfinite<'TKey,'TValue when 'TKey : equality> (source: 'TValue) : IDictionary<'TKey,'TValue> = new DefaultableDict<'TKey,'TValue>(source, Dictionary<'TKey,'TValue> ()) - let initHybrid<'TKey,'TValue> (konst: 'TValue) (source: IDictionary<'TKey,'TValue>) : IDictionary<'TKey,'TValue> = new DefaultableDict<'TKey,'TValue>(konst, source) + /// Creates a defaultable dictionary. + /// The value for all missing keys. + /// The source dictionary. + let emptyWithDefault<'TKey,'TValue when 'TKey : equality> (konst: 'TValue) : IDictionary<'TKey,'TValue> = new DefaultableDict<'TKey,'TValue>(konst, dict []) + + /// Creates a defaultable dictionary. + /// The value for all missing keys. + /// The source dictionary. + let initWithDefault<'TKey,'TValue> (konst: 'TValue) (source: IDictionary<'TKey,'TValue>) : IDictionary<'TKey,'TValue> = new DefaultableDict<'TKey,'TValue>(konst, source) #if !FABLE_COMPILER open System.Linq @@ -128,18 +125,13 @@ module Dict = /// /// The mapped dictionary. let map mapper (source: IDictionary<'Key, 'T>) = - match source with - | :? DefaultableDict<'Key, 'T> as s -> - let dct = DefaultableDict<'Key, 'U> (mapper s.DefaultValue, Dictionary<'Key, 'U> ()) - let dct = dct :> IDictionary<'Key, 'U> - for KeyValue(k, v) in source do - dct.Add (k, mapper v) - dct - | _ -> - let dct = Dictionary<'Key, 'U> () - for KeyValue(k, v) in source do - dct.Add (k, mapper v) - dct :> IDictionary<'Key, 'U> + let dct = + match source with + | :? DefaultableDict<'Key, 'T> as s -> emptyWithDefault (mapper s.DefaultValue) + | _ -> Dictionary<'Key, 'U> () :> IDictionary<'Key, 'U> + for KeyValue(k, v) in source do + dct.Add (k, mapper v) + dct /// Creates a Dictionary value from a pair of Dictionaries, using a function to combine them. /// Keys that are not present on both dictionaries are dropped. @@ -149,38 +141,41 @@ module Dict = /// /// The combined dictionary. let map2 mapper (source1: IDictionary<'Key, 'T1>) (source2: IDictionary<'Key, 'T2>) = - let map k1 k2 = + let map () = let dct = Dictionary<'Key, 'U> () let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt mapper - for k in set source1.Keys + set source2.Keys do - match tryGetValue k source1, tryGetValue k source2, k1, k2 with - | Some vx, Some vy, _ , _ - | None , Some vy, Some vx, _ - | Some vx, None , _ , Some vy -> dct.Add (k, f.Invoke (vx, vy)) - | _ , _ , _ , _ -> () + let keys = Seq.append source1.Keys source2.Keys |> Seq.distinct + for k in keys do + match tryGetValue k source1, tryGetValue k source2 with + | Some vx, Some vy -> dct.Add (k, f.Invoke (vx, vy)) + | _ , _ -> () dct :> IDictionary<'Key, 'U> match source1, source2 with - | (:? DefaultableDict<'Key,'T1> as s1), (:? DefaultableDict<'Key,'T2> as s2) -> initHybrid (mapper s1.DefaultValue s2.DefaultValue) (map (Some s1.DefaultValue) (Some s2.DefaultValue)) - | (:? DefaultableDict<'Key,'T1> as s1), _ -> map (Some s1.DefaultValue) None - | _, (:? DefaultableDict<'Key,'T2> as s2) -> map None (Some s2.DefaultValue) - | _, _ -> map None None + | (:? DefaultableDict<'Key,'T1> as s1), (:? DefaultableDict<'Key,'T2> as s2) -> initWithDefault (mapper s1.DefaultValue s2.DefaultValue) (map ()) + | _, _ -> map () /// Combines values from three dictionaries using mapping function. /// Keys that are not present on every dictionary are dropped. - /// The mapping function. + /// The mapping function. /// First input dictionary. /// Second input dictionary. /// Third input dictionary. /// /// The mapped dictionary. - let map3 mapping (source1: IDictionary<'Key, 'T1>) (source2: IDictionary<'Key, 'T2>) (source3: IDictionary<'Key, 'T3>) = - let dct = Dictionary<'Key, 'U> () - let f = OptimizedClosures.FSharpFunc<_,_,_,_>.Adapt mapping - for KeyValue(k, vx) in source1 do - match tryGetValue k source2, tryGetValue k source3 with - | Some vy, Some vz -> dct.Add (k, f.Invoke (vx, vy, vz)) - | _ , _ -> () - dct :> IDictionary<'Key, 'U> + let map3 mapper (source1: IDictionary<'Key, 'T1>) (source2: IDictionary<'Key, 'T2>) (source3: IDictionary<'Key, 'T3>) = + let map () = + let dct = Dictionary<'Key, 'U> () + let f = OptimizedClosures.FSharpFunc<_,_,_,_>.Adapt mapper + let keys = source1.Keys |> Seq.append source2.Keys |> Seq.append source3.Keys |> Seq.distinct + for k in keys do + match tryGetValue k source1, tryGetValue k source2, tryGetValue k source3 with + | Some vx, Some vy, Some vz -> dct.Add (k, f.Invoke (vx, vy, vz)) + | _ , _ , _ -> () + dct :> IDictionary<'Key, 'U> + match source1, source2, source3 with + | (:? DefaultableDict<'Key,'T1> as s1), (:? DefaultableDict<'Key,'T2> as s2), (:? DefaultableDict<'Key,'T3> as s3) -> + initWithDefault (mapper s1.DefaultValue s2.DefaultValue s3.DefaultValue) (map ()) + | _, _, _ -> map () /// Applies given function to each value of the given dictionary. /// The mapping function. @@ -223,17 +218,9 @@ module Dict = for KeyValue(k, v ) in source1 do d.[k] <- v for KeyValue(k, v') in source2 do d.[k] <- match d.TryGetValue k with true, v -> f.Invoke (v, v') | _ -> v' d :> IDictionary<'Key,'Value> - // let combineWithKonst source konst = - // let d = Dictionary<'Key,'Value> () - // for KeyValue(k, v) in source do d.[k] <- combiner v konst - // d :> IDictionary<'Key,'Value> - // let combineKonstWith konst source = - // let d = Dictionary<'Key,'Value> () - // for KeyValue(k, v) in source do d.[k] <- combiner konst v - // d :> IDictionary<'Key,'Value> match source1, source2 with - | (:? DefaultableDict<'Key,'Value> as s1), (:? DefaultableDict<'Key,'Value> as s2) -> initHybrid (combiner s1.DefaultValue s2.DefaultValue) (combine()) - | (:? DefaultableDict<'Key,'Value> as s), _ | _, (:? DefaultableDict<'Key,'Value> as s) -> initHybrid s.DefaultValue (combine()) + | (:? DefaultableDict<'Key,'Value> as s1) , (:? DefaultableDict<'Key,'Value> as s2) -> initWithDefault (combiner s1.DefaultValue s2.DefaultValue) (combine()) + | (:? DefaultableDict<'Key,'Value> as s), _ | _, (:? DefaultableDict<'Key,'Value> as s) -> initWithDefault s.DefaultValue (combine()) | s, empty | empty, s when empty.Count = 0 -> s | _, _ -> combine()