Skip to content

Commit 33fdc1e

Browse files
committed
Allow unioning them
1 parent 88bd6b9 commit 33fdc1e

File tree

1 file changed

+120
-45
lines changed

1 file changed

+120
-45
lines changed

src/FSharpPlus/Extensions/Dict.fs

Lines changed: 120 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,128 @@ module Dict =
129129
dct2.Add (k, vy)
130130
dct1 :> IDictionary<'Key, 'T1>, dct2 :> IDictionary<'Key, 'T2>
131131

132+
133+
/// <summary>Creates a conceptually infinite dictionay containing the same value for all possible keys.</summary>
134+
/// <param name="source">The value for all possible keys.</param>
135+
let initInfinite<'TKey,'TValue> (source: 'TValue) : IDictionary<'TKey,'TValue> =
136+
137+
let icollection value =
138+
{
139+
new ICollection<'t> with
140+
member __.Contains (item: 't) = obj.ReferenceEquals (item, value)
141+
member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () :> System.Collections.IEnumerator
142+
member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () : IEnumerator<'t>
143+
member __.IsReadOnly = true
144+
145+
member __.Add (_item: 't) : unit = raise (System.NotImplementedException())
146+
member __.Clear () : unit = raise (System.NotImplementedException())
147+
member __.CopyTo (_array: 't [], _arrayIndex: int) : unit = raise (System.NotImplementedException())
148+
member __.Count : int = -1
149+
member __.Remove (_item: 't): bool = raise (System.NotImplementedException())
150+
}
151+
152+
{
153+
new IDictionary<'TKey,'TValue> with
154+
member __.TryGetValue (_key: 'TKey, value: byref<'TValue>) = value <- source; true
155+
member __.Count = -1
156+
member __.ContainsKey (_key: 'TKey) = true
157+
member __.Contains (item: KeyValuePair<'TKey,'TValue>) = obj.ReferenceEquals (item.Value, source)
158+
member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : System.Collections.IEnumerator
159+
member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : IEnumerator<KeyValuePair<'TKey,'TValue>>
160+
member __.IsReadOnly = true
161+
member __.Values = icollection source
162+
member __.Item
163+
with get (_key: 'TKey) : 'TValue = source
164+
and set (_key: 'TKey) (_: 'TValue) : unit = raise (System.NotImplementedException())
165+
166+
member __.Add (_key: 'TKey, _value: 'TValue) : unit = raise (System.NotImplementedException())
167+
member __.Add (_item: KeyValuePair<'TKey,'TValue>) : unit = raise (System.NotImplementedException())
168+
member __.Clear () : unit = raise (System.NotImplementedException())
169+
member __.CopyTo (_arr: KeyValuePair<'TKey,'TValue> [], _arrayIndex: int) : unit = raise (System.NotImplementedException())
170+
member __.Keys : ICollection<'TKey> = raise (System.NotImplementedException())
171+
member __.Remove (_key: 'TKey) : bool = raise (System.NotImplementedException())
172+
member __.Remove (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException())
173+
}
174+
175+
let initHybrid<'TKey,'TValue> (konst: 'TValue) (source: IDictionary<'TKey,'TValue>) : IDictionary<'TKey,'TValue> =
176+
177+
let icollection (konst: 'TValue) (source: IDictionary<'TKey,'TValue>) =
178+
{
179+
new ICollection<'t> with
180+
member __.Contains (item: 't) = source.Values.Contains item || obj.ReferenceEquals (item, konst)
181+
member __.GetEnumerator () = (seq { yield! source.Values; yield! (Seq.initInfinite (fun _ -> konst))}).GetEnumerator () :> System.Collections.IEnumerator
182+
member __.GetEnumerator () = (seq { yield! source.Values; yield! (Seq.initInfinite (fun _ -> konst))}).GetEnumerator () : IEnumerator<'t>
183+
member __.IsReadOnly = true
184+
185+
member __.Add (_item: 't) : unit = raise (System.NotImplementedException())
186+
member __.Clear () : unit = raise (System.NotImplementedException())
187+
member __.CopyTo (_array: 't [], _arrayIndex: int) : unit = raise (System.NotImplementedException())
188+
member __.Count : int = -source.Count-1
189+
member __.Remove (_item: 't): bool = raise (System.NotImplementedException())
190+
}
191+
{
192+
new IDictionary<'TKey,'TValue> with
193+
member __.TryGetValue (key: 'TKey, value: byref<'TValue>) =
194+
match source.TryGetValue key with
195+
| true, v -> value <- v
196+
| _ -> value <- konst
197+
true
198+
member __.Count = -source.Count-1
199+
member __.ContainsKey (_key: 'TKey) = true
200+
member __.Contains (item: KeyValuePair<'TKey,'TValue>) =
201+
match source.TryGetValue item.Key with
202+
| true, v -> obj.ReferenceEquals (item.Value, v)
203+
| _ -> obj.ReferenceEquals (item.Value, konst)
204+
member __.GetEnumerator () = source.GetEnumerator () : System.Collections.IEnumerator
205+
member __.GetEnumerator () = source.GetEnumerator () : IEnumerator<KeyValuePair<'TKey,'TValue>>
206+
member __.IsReadOnly = true
207+
member __.Values = icollection konst source
208+
member __.Item
209+
with get (key: 'TKey) : 'TValue = match source.TryGetValue key with (true, v) -> v | _ -> konst
210+
and set (_key: 'TKey) (_: 'TValue) : unit = raise (System.NotImplementedException())
211+
212+
member __.Add (_key: 'TKey, _value: 'TValue) : unit = raise (System.NotImplementedException())
213+
member __.Add (_item: KeyValuePair<'TKey,'TValue>) : unit = raise (System.NotImplementedException())
214+
member __.Clear () : unit = raise (System.NotImplementedException())
215+
member __.CopyTo (_arr: KeyValuePair<'TKey,'TValue> [], _arrayIndex: int) : unit = raise (System.NotImplementedException())
216+
member __.Keys : ICollection<'TKey> = raise (System.NotImplementedException())
217+
member __.Remove (_key: 'TKey) : bool = raise (System.NotImplementedException())
218+
member __.Remove (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException())
219+
}
220+
132221
/// Returns the union of two dictionaries, using the combiner function for duplicate keys.
133222
let unionWith combiner (source1: IDictionary<'Key, 'Value>) (source2: IDictionary<'Key, 'Value>) =
134-
let d = Dictionary<'Key,'Value> ()
135-
let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt combiner
136-
for KeyValue(k, v ) in source1 do d.[k] <- v
137-
for KeyValue(k, v') in source2 do d.[k] <- match d.TryGetValue k with true, v -> f.Invoke (v, v') | _ -> v'
138-
d :> IDictionary<'Key,'Value>
223+
let combine () =
224+
let d = Dictionary<'Key,'Value> ()
225+
let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt combiner
226+
for KeyValue(k, v ) in source1 do d.[k] <- v
227+
for KeyValue(k, v') in source2 do d.[k] <- match d.TryGetValue k with true, v -> f.Invoke (v, v') | _ -> v'
228+
d :> IDictionary<'Key,'Value>
229+
let combineWithKonst source konst =
230+
let d = Dictionary<'Key,'Value> ()
231+
for KeyValue(k, v) in source do d.[k] <- combiner v konst
232+
d :> IDictionary<'Key,'Value>
233+
let combineKonstWith konst source =
234+
let d = Dictionary<'Key,'Value> ()
235+
for KeyValue(k, v) in source do d.[k] <- combiner konst v
236+
d :> IDictionary<'Key,'Value>
237+
match source1.Count, source2.Count with
238+
| -1, -1 ->
239+
initInfinite (
240+
combiner
241+
(source1[Unchecked.defaultof<'Key>])
242+
(source2[Unchecked.defaultof<'Key>]))
243+
| -1, 0 -> source1
244+
| 0, -1 -> source2
245+
| -1, x when x < -1 -> initHybrid (combiner (source1[Unchecked.defaultof<'Key>]) (source2[Unchecked.defaultof<'Key>])) (combineKonstWith (source1[Unchecked.defaultof<'Key>]) source2)
246+
| x, -1 when x < -1 -> initHybrid (combiner (source1[Unchecked.defaultof<'Key>]) (source2[Unchecked.defaultof<'Key>])) (combineWithKonst source1 (source2[Unchecked.defaultof<'Key>]))
247+
// Note: this is horrible and Unchecked.defaultof<'Key> is 0 for int keys, so it might not return the default value.
248+
// 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.
249+
| x, y when x < -1 && y < -1 -> initHybrid (combiner (source1[Unchecked.defaultof<'Key>]) (source2[Unchecked.defaultof<'Key>])) (combine())
250+
| -1, _ -> combineKonstWith (source1[Unchecked.defaultof<'Key>]) source2 |> initHybrid (source1[Unchecked.defaultof<'Key>])
251+
| _, -1 -> combineWithKonst source1 (source2[Unchecked.defaultof<'Key>]) |> initHybrid (source2[Unchecked.defaultof<'Key>])
252+
| _, _ -> combine()
253+
139254

140255
#if !FABLE_COMPILER
141256
///Returns the union of two maps, preferring values from the first in case of duplicate keys.
@@ -179,44 +294,4 @@ module Dict =
179294
| None -> ()
180295
dct :> IDictionary<'Key, 'U>
181296

182-
/// <summary>Creates a conceptually infinite dictionay containing the same value for all possible keys.</summary>
183-
/// <param name="source">The value for all possible keys.</param>
184-
let initInfinite<'TKey,'TValue> (source: 'TValue) : IDictionary<'TKey,'TValue> =
185-
186-
let icollection value =
187-
{
188-
new ICollection<'t> with
189-
member __.Contains (item: 't) = obj.ReferenceEquals (item, value)
190-
member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () :> System.Collections.IEnumerator
191-
member __.GetEnumerator () = (Seq.initInfinite (fun _ -> value)).GetEnumerator () : IEnumerator<'t>
192-
member __.IsReadOnly = true
193-
194-
member __.Add (_item: 't) : unit = raise (System.NotImplementedException())
195-
member __.Clear () : unit = raise (System.NotImplementedException())
196-
member __.CopyTo (_array: 't [], _arrayIndex: int) : unit = raise (System.NotImplementedException())
197-
member __.Count : int = raise (System.NotImplementedException())
198-
member __.Remove (_item: 't): bool = raise (System.NotImplementedException())
199-
}
200-
201-
{
202-
new IDictionary<'TKey,'TValue> with
203-
member __.TryGetValue (_key: 'TKey, value: byref<'TValue>) = value <- source; true
204-
member __.Count = System.Int32.MaxValue
205-
member __.ContainsKey (_key: 'TKey) = true
206-
member __.Contains (item: KeyValuePair<'TKey,'TValue>) = obj.ReferenceEquals (item.Value, source)
207-
member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : System.Collections.IEnumerator
208-
member __.GetEnumerator () = invalidOp "Key set is potentially infinite." : IEnumerator<KeyValuePair<'TKey,'TValue>>
209-
member __.IsReadOnly = true
210-
member __.Values = icollection source
211-
member __.Item
212-
with get (_key: 'TKey) : 'TValue = source
213-
and set (_key: 'TKey) (_: 'TValue) : unit = raise (System.NotImplementedException())
214297

215-
member __.Add (_key: 'TKey, _value: 'TValue) : unit = raise (System.NotImplementedException())
216-
member __.Add (_item: KeyValuePair<'TKey,'TValue>) : unit = raise (System.NotImplementedException())
217-
member __.Clear () : unit = raise (System.NotImplementedException())
218-
member __.CopyTo (_arr: KeyValuePair<'TKey,'TValue> [], _arrayIndex: int) : unit = raise (System.NotImplementedException())
219-
member __.Keys : ICollection<'TKey> = raise (System.NotImplementedException())
220-
member __.Remove (_key: 'TKey) : bool = raise (System.NotImplementedException())
221-
member __.Remove (_item: KeyValuePair<'TKey,'TValue>) : bool = raise (System.NotImplementedException())
222-
}

0 commit comments

Comments
 (0)