diff --git a/src/fsharp/FSharp.Core/fslib-extra-pervasives.fs b/src/fsharp/FSharp.Core/fslib-extra-pervasives.fs index 25aab8048aa..63cb2d80718 100644 --- a/src/fsharp/FSharp.Core/fslib-extra-pervasives.fs +++ b/src/fsharp/FSharp.Core/fslib-extra-pervasives.fs @@ -40,59 +40,95 @@ module ExtraTopLevelOperators = let inline ICollection_Contains<'collection,'item when 'collection :> ICollection<'item>> (collection:'collection) (item:'item) = collection.Contains item - let inline dictImpl (comparer:IEqualityComparer<'SafeKey>) (makeSafeKey:'Key->'SafeKey) (getKey:'SafeKey->'Key) (l:seq<'Key*'T>) = + [] + [>)>] + type DictImpl<'SafeKey,'Key,'T>(t : Dictionary<'SafeKey,'T>, makeSafeKey : 'Key->'SafeKey, getKey : 'SafeKey->'Key) = + + member x.Count = t.Count + + // Give a read-only view of the dictionary + interface IDictionary<'Key, 'T> with + member s.Item + with get x = dont_tail_call (fun () -> t.[makeSafeKey x]) + and set _ _ = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))) + member s.Keys = + let keys = t.Keys + { new ICollection<'Key> with + member s.Add(x) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); + member s.Clear() = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); + member s.Remove(x) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); + member s.Contains(x) = t.ContainsKey (makeSafeKey x) + member s.CopyTo(arr,i) = + let mutable n = 0 + for k in keys do + arr.[i+n] <- getKey k + n <- n + 1 + member s.IsReadOnly = true + member s.Count = keys.Count + interface IEnumerable<'Key> with + member s.GetEnumerator() = (keys |> Seq.map getKey).GetEnumerator() + interface System.Collections.IEnumerable with + member s.GetEnumerator() = ((keys |> Seq.map getKey) :> System.Collections.IEnumerable).GetEnumerator() } + + member s.Values = upcast t.Values + member s.Add(_,_) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))) + member s.ContainsKey(k) = dont_tail_call (fun () -> t.ContainsKey(makeSafeKey k)) + member s.TryGetValue(k,r) = + let safeKey = makeSafeKey k + if t.ContainsKey(safeKey) then (r <- t.[safeKey]; true) else false + member s.Remove(_ : 'Key) = (raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))) : bool) + + interface IReadOnlyDictionary<'Key, 'T> with + member __.Item with get key = t.[makeSafeKey key] + member __.Keys = t.Keys |> Seq.map getKey + member __.TryGetValue(key, r) = + match t.TryGetValue (makeSafeKey key) with + | false, _ -> false + | true, value -> + r <- value + true + member __.Values = (t :> IReadOnlyDictionary<_,_>).Values + member __.ContainsKey k = t.ContainsKey (makeSafeKey k) + + interface ICollection> with + member s.Add(_) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); + member s.Clear() = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); + member s.Remove(_) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); + member s.Contains(KeyValue(k,v)) = ICollection_Contains t (KeyValuePair<_,_>(makeSafeKey k,v)) + member s.CopyTo(arr,i) = + let mutable n = 0 + for (KeyValue(k,v)) in t do + arr.[i+n] <- KeyValuePair<_,_>(getKey k,v) + n <- n + 1 + member s.IsReadOnly = true + member s.Count = t.Count + + interface IReadOnlyCollection> with + member __.Count = t.Count + + interface IEnumerable> with + member s.GetEnumerator() = + // We use an array comprehension here instead of seq {} as otherwise we get incorrect + // IEnumerator.Reset() and IEnumerator.Current semantics. + let kvps = [| for (KeyValue (k,v)) in t -> KeyValuePair (getKey k, v) |] :> seq<_> + kvps.GetEnumerator() + + interface System.Collections.IEnumerable with + member s.GetEnumerator() = + // We use an array comprehension here instead of seq {} as otherwise we get incorrect + // IEnumerator.Reset() and IEnumerator.Current semantics. + let kvps = [| for (KeyValue (k,v)) in t -> KeyValuePair (getKey k, v) |] :> System.Collections.IEnumerable + kvps.GetEnumerator() + + and DictDebugView<'SafeKey,'Key,'T>(d:DictImpl<'SafeKey,'Key,'T>) = + [] + member x.Items = Array.ofSeq d + + let inline dictImpl (comparer:IEqualityComparer<'SafeKey>) (makeSafeKey : 'Key->'SafeKey) (getKey : 'SafeKey->'Key) (l:seq<'Key*'T>) = let t = Dictionary comparer - for (k,v) in l do + for (k,v) in l do t.[makeSafeKey k] <- v - // Give a read-only view of the dictionary - { new IDictionary<'Key, 'T> with - member s.Item - with get x = dont_tail_call (fun () -> t.[makeSafeKey x]) - and set x v = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))) - member s.Keys = - let keys = t.Keys - { new ICollection<'Key> with - member s.Add(x) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); - member s.Clear() = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); - member s.Remove(x) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); - member s.Contains(x) = t.ContainsKey (makeSafeKey x) - member s.CopyTo(arr,i) = - let mutable n = 0 - for k in keys do - arr.[i+n] <- getKey k - n <- n + 1 - member s.IsReadOnly = true - member s.Count = keys.Count - interface IEnumerable<'Key> with - member s.GetEnumerator() = (keys |> Seq.map getKey).GetEnumerator() - interface System.Collections.IEnumerable with - member s.GetEnumerator() = ((keys |> Seq.map getKey) :> System.Collections.IEnumerable).GetEnumerator() } - - member s.Values = upcast t.Values - member s.Add(k,v) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))) - member s.ContainsKey(k) = dont_tail_call (fun () -> t.ContainsKey(makeSafeKey k)) - member s.TryGetValue(k,r) = - let safeKey = makeSafeKey k - if t.ContainsKey(safeKey) then (r <- t.[safeKey]; true) else false - member s.Remove(k : 'Key) = (raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))) : bool) - interface ICollection> with - member s.Add(x) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); - member s.Clear() = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); - member s.Remove(x) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); - member s.Contains(KeyValue(k,v)) = ICollection_Contains t (KeyValuePair<_,_>(makeSafeKey k,v)) - member s.CopyTo(arr,i) = - let mutable n = 0 - for (KeyValue(k,v)) in t do - arr.[i+n] <- KeyValuePair<_,_>(getKey k,v) - n <- n + 1 - member s.IsReadOnly = true - member s.Count = t.Count - interface IEnumerable> with - member s.GetEnumerator() = - (t |> Seq.map (fun (KeyValue(k,v)) -> KeyValuePair<_,_>(getKey k,v))).GetEnumerator() - interface System.Collections.IEnumerable with - member s.GetEnumerator() = - ((t |> Seq.map (fun (KeyValue(k,v)) -> KeyValuePair<_,_>(getKey k,v))) :> System.Collections.IEnumerable).GetEnumerator() } + DictImpl(t, makeSafeKey, getKey) // We avoid wrapping a StructBox, because under 64 JIT we get some "hard" tailcalls which affect performance let dictValueType (l:seq<'Key*'T>) = dictImpl HashIdentity.Structural<'Key> id id l @@ -101,14 +137,24 @@ module ExtraTopLevelOperators = let dictRefType (l:seq<'Key*'T>) = dictImpl RuntimeHelpers.StructBox<'Key>.Comparer (fun k -> RuntimeHelpers.StructBox k) (fun sb -> sb.Value) l [] - let dict (keyValuePairs:seq<'Key*'T>) = + let dict (keyValuePairs:seq<'Key*'T>) : IDictionary<'Key,'T> = +#if FX_RESHAPED_REFLECTION + if (typeof<'Key>).GetTypeInfo().IsValueType +#else + if typeof<'Key>.IsValueType +#endif + then dictValueType keyValuePairs :> _ + else dictRefType keyValuePairs :> _ + + [] + let readOnlyDict (keyValuePairs:seq<'Key*'T>) : IReadOnlyDictionary<'Key,'T> = #if FX_RESHAPED_REFLECTION if (typeof<'Key>).GetTypeInfo().IsValueType #else if typeof<'Key>.IsValueType #endif - then dictValueType keyValuePairs - else dictRefType keyValuePairs + then dictValueType keyValuePairs :> _ + else dictRefType keyValuePairs :> _ let getArray (vals : seq<'T>) = match vals with diff --git a/src/fsharp/FSharp.Core/fslib-extra-pervasives.fsi b/src/fsharp/FSharp.Core/fslib-extra-pervasives.fsi index 6c96eda32a4..6cfbd860c47 100644 --- a/src/fsharp/FSharp.Core/fslib-extra-pervasives.fsi +++ b/src/fsharp/FSharp.Core/fslib-extra-pervasives.fsi @@ -122,6 +122,10 @@ module ExtraTopLevelOperators = [] val dict : keyValuePairs:seq<'Key * 'Value> -> System.Collections.Generic.IDictionary<'Key,'Value> when 'Key : equality + /// Builds a read-only lookup table from a sequence of key/value pairs. The key objects are indexed using generic hashing and equality. + [] + val readOnlyDict : keyValuePairs:seq<'Key * 'Value> -> System.Collections.Generic.IReadOnlyDictionary<'Key,'Value> when 'Key : equality + /// Builds a 2D array from a sequence of sequences of elements. [] val array2D : rows:seq<#seq<'T>> -> 'T[,] diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.Unittests.fsproj b/tests/FSharp.Core.UnitTests/FSharp.Core.Unittests.fsproj index 6bdebe09cce..bfe62f1974d 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.Unittests.fsproj +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.Unittests.fsproj @@ -39,7 +39,7 @@ prompt 3 - + true @@ -105,6 +105,7 @@ + @@ -125,4 +126,4 @@ - + \ No newline at end of file diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Core/ExtraTopLevelOperatorsTests.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Core/ExtraTopLevelOperatorsTests.fs new file mode 100644 index 00000000000..f861189ae8f --- /dev/null +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Core/ExtraTopLevelOperatorsTests.fs @@ -0,0 +1,222 @@ +namespace FSharp.Core.UnitTests.FSharp_Core.Microsoft_FSharp_Core + +open NUnit.Framework +open FSharp.Core.UnitTests.LibraryTestFx +open System.Collections +open System.Collections.Generic + +[] +type DictTests () = + + [] + member this.IEnumerable() = + // Legit IE + let ie = (dict [|(1,1);(2,4);(3,9)|]) :> IEnumerable + let enum = ie.GetEnumerator() + + let testStepping() = + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) + Assert.AreEqual(enum.MoveNext(), true) + Assert.AreEqual(enum.Current, new KeyValuePair(1,1)) + + Assert.AreEqual(enum.MoveNext(), true) + Assert.AreEqual(enum.Current, new KeyValuePair(2,4)) + Assert.AreEqual(enum.MoveNext(), true) + Assert.AreEqual(enum.Current, new KeyValuePair(3,9)) + Assert.AreEqual(enum.MoveNext(), false) + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) + + testStepping() + enum.Reset() + testStepping() + + // Empty IE + let ie = [] |> dict :> IEnumerable // Note no type args + let enum = ie.GetEnumerator() + + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) + Assert.AreEqual(enum.MoveNext(), false) + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) + + [] + member this.IEnumerable_T() = + // Legit IE + let ie = (dict [|(1,1);(2,4);(3,9)|]) :> IEnumerable> + let enum = ie.GetEnumerator() + + let testStepping() = + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) + Assert.AreEqual(enum.MoveNext(), true) + Assert.AreEqual(enum.Current, new KeyValuePair(1,1)) + + Assert.AreEqual(enum.MoveNext(), true) + Assert.AreEqual(enum.Current, new KeyValuePair(2,4)) + Assert.AreEqual(enum.MoveNext(), true) + Assert.AreEqual(enum.Current, new KeyValuePair(3,9)) + Assert.AreEqual(enum.MoveNext(), false) + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) + + testStepping() + enum.Reset() + testStepping() + + // Empty IE + let ie = [] |> dict :> IEnumerable // Note no type args + let enum = ie.GetEnumerator() + + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) + Assert.AreEqual(enum.MoveNext(), false) + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) + + + [] + member this.IDictionary() = + // Legit ID + let id = (dict [|(1,1);(2,4);(3,9)|]) :> IDictionary<_,_> + + Assert.IsTrue(id.ContainsKey(1)) + Assert.IsFalse(id.ContainsKey(5)) + Assert.AreEqual(id.[1], 1) + Assert.AreEqual(id.[3], 9) + Assert.AreEqual(id.Keys, [| 1; 2; 3|]) + Assert.AreEqual(id.Values, [| 1; 4; 9|]) + + CheckThrowsNotSupportedException(fun () -> id.[2] <-88) + + CheckThrowsNotSupportedException(fun () -> id.Add(new KeyValuePair(4,16))) + let mutable value = 0 + Assert.IsTrue(id.TryGetValue(2, &value)) + Assert.AreEqual(4, value) + Assert.IsFalse(id.TryGetValue(100, &value)) + Assert.AreEqual(4, value) + CheckThrowsNotSupportedException(fun () -> id.Remove(1) |> ignore) + + // Empty ID + let id = dict [] :> IDictionary // Note no type args + Assert.IsFalse(id.ContainsKey(5)) + CheckThrowsKeyNotFoundException(fun () -> id.[1] |> ignore) + Assert.AreEqual(id.Keys, [| |] ) + Assert.AreEqual(id.Values, [| |] ) + + [] + member this.``IReadOnlyDictionary on readOnlyDict``() = + let irod = (readOnlyDict [|(1,1);(2,4);(3,9)|]) :> IReadOnlyDictionary<_,_> + + Assert.IsTrue(irod.ContainsKey(1)) + Assert.IsFalse(irod.ContainsKey(5)) + Assert.AreEqual(irod.[1], 1) + Assert.AreEqual(irod.[3], 9) + Assert.AreEqual(irod.Keys, [| 1; 2; 3|]) + Assert.AreEqual(irod.Values, [| 1; 4; 9|]) + + let mutable value = 0 + Assert.IsTrue(irod.TryGetValue(2, &value)) + Assert.AreEqual(4, value) + + Assert.IsFalse(irod.TryGetValue(100, &value)) + + // value should not have been modified + Assert.AreEqual(4, value) + + // Empty IROD + let irod = readOnlyDict [] :> IReadOnlyDictionary // Note no type args + Assert.IsFalse(irod.ContainsKey(5)) + CheckThrowsKeyNotFoundException(fun () -> irod.[1] |> ignore) + Assert.AreEqual(irod.Keys, [| |] ) + Assert.AreEqual(irod.Values, [| |] ) + + [] + member this.ICollection() = + // Legit IC + let ic = (dict [|(1,1);(2,4);(3,9)|]) :> ICollection> + + Assert.AreEqual(ic.Count, 3) + Assert.IsTrue(ic.Contains(new KeyValuePair(3,9))) + let newArr = Array.create 5 (new KeyValuePair(3,9)) + ic.CopyTo(newArr,0) + Assert.IsTrue(ic.IsReadOnly) + + + // raise ReadOnlyCollection exception + CheckThrowsNotSupportedException(fun () -> ic.Add(new KeyValuePair(3,9)) |> ignore) + CheckThrowsNotSupportedException(fun () -> ic.Clear() |> ignore) + CheckThrowsNotSupportedException(fun () -> ic.Remove(new KeyValuePair(3,9)) |> ignore) + + + // Empty IC + let ic = dict [] :> ICollection> + Assert.IsFalse(ic.Contains(new KeyValuePair(3,9))) + let newArr = Array.create 5 (new KeyValuePair(0,0)) + ic.CopyTo(newArr,0) + + [] + member this.``IReadOnlyCollection on readOnlyDict``() = + // Legit IROC + let iroc = (readOnlyDict [|(1,1);(2,4);(3,9)|]) :> IReadOnlyCollection> + + Assert.AreEqual(iroc.Count, 3) + + // Empty IROC + let iroc = readOnlyDict [] :> IReadOnlyCollection> + + Assert.AreEqual(iroc.Count, 0) + + [] + member this.``IEnumerable on readOnlyDict``() = + // Legit IE + let ie = (readOnlyDict [|(1,1);(2,4);(3,9)|]) :> IEnumerable + let enum = ie.GetEnumerator() + + let testStepping() = + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) + Assert.AreEqual(enum.MoveNext(), true) + Assert.AreEqual(enum.Current, new KeyValuePair(1,1)) + + Assert.AreEqual(enum.MoveNext(), true) + Assert.AreEqual(enum.Current, new KeyValuePair(2,4)) + Assert.AreEqual(enum.MoveNext(), true) + Assert.AreEqual(enum.Current, new KeyValuePair(3,9)) + Assert.AreEqual(enum.MoveNext(), false) + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) + + testStepping() + enum.Reset() + testStepping() + + // Empty IE + let ie = [] |> readOnlyDict :> IEnumerable // Note no type args + let enum = ie.GetEnumerator() + + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) + Assert.AreEqual(enum.MoveNext(), false) + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) + + [] + member this.``IEnumerable_T on readOnlyDict``() = + // Legit IE + let ie = (readOnlyDict [|(1,1);(2,4);(3,9)|]) :> IEnumerable> + let enum = ie.GetEnumerator() + + let testStepping() = + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) + Assert.AreEqual(enum.MoveNext(), true) + Assert.AreEqual(enum.Current, new KeyValuePair(1,1)) + + Assert.AreEqual(enum.MoveNext(), true) + Assert.AreEqual(enum.Current, new KeyValuePair(2,4)) + Assert.AreEqual(enum.MoveNext(), true) + Assert.AreEqual(enum.Current, new KeyValuePair(3,9)) + Assert.AreEqual(enum.MoveNext(), false) + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) + + testStepping() + enum.Reset() + testStepping() + + // Empty IE + let ie = [] |> readOnlyDict :> IEnumerable // Note no type args + let enum = ie.GetEnumerator() + + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) + Assert.AreEqual(enum.MoveNext(), false) + CheckThrowsInvalidOperationExn(fun () -> enum.Current |> ignore) diff --git a/tests/FSharp.Core.UnitTests/LibraryTestFx.fs b/tests/FSharp.Core.UnitTests/LibraryTestFx.fs index 1d122d213cb..b58653a1de1 100644 --- a/tests/FSharp.Core.UnitTests/LibraryTestFx.fs +++ b/tests/FSharp.Core.UnitTests/LibraryTestFx.fs @@ -13,17 +13,12 @@ open NUnit.Framework /// Check that the lambda throws an exception of the given type. Otherwise /// calls Assert.Fail() let CheckThrowsExn<'a when 'a :> exn> (f : unit -> unit) = - let funcThrowsAsExpected = - try - let _ = f () - false // Did not throw! - with - | :? 'a - -> true // Thew null ref, OK - | _ -> false // Did now throw a null ref exception! - if funcThrowsAsExpected - then () - else Assert.Fail() + try + let _ = f () + sprintf "Expected %O exception, got no exception" typeof<'a> |> Assert.Fail + with + | :? 'a -> () + | e -> sprintf "Expected %O exception, got: %O" typeof<'a> e |> Assert.Fail let private CheckThrowsExn2<'a when 'a :> exn> s (f : unit -> unit) = let funcThrowsAsExpected = diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs index 1c9d7c18c19..7ab7de96ee1 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs @@ -1106,6 +1106,7 @@ Microsoft.FSharp.Core.ExtraTopLevelOperators: Microsoft.FSharp.Linq.QueryBuilder Microsoft.FSharp.Core.ExtraTopLevelOperators: SByte ToSByte[T](T) Microsoft.FSharp.Core.ExtraTopLevelOperators: Single ToSingle[T](T) Microsoft.FSharp.Core.ExtraTopLevelOperators: System.Collections.Generic.IDictionary`2[TKey,TValue] CreateDictionary[TKey,TValue](System.Collections.Generic.IEnumerable`1[System.Tuple`2[TKey,TValue]]) +Microsoft.FSharp.Core.ExtraTopLevelOperators: System.Collections.Generic.IReadOnlyDictionary`2[TKey,TValue] CreateReadOnlyDictionary[TKey,TValue](System.Collections.Generic.IEnumerable`1[System.Tuple`2[TKey,TValue]]) Microsoft.FSharp.Core.ExtraTopLevelOperators: System.String ToString() Microsoft.FSharp.Core.ExtraTopLevelOperators: System.Type GetType() Microsoft.FSharp.Core.ExtraTopLevelOperators: T LazyPattern[T](System.Lazy`1[T]) diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs index 87ed5adff4a..4e1bb004233 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs @@ -1179,6 +1179,7 @@ Microsoft.FSharp.Core.ExtraTopLevelOperators: Microsoft.FSharp.Linq.QueryBuilder Microsoft.FSharp.Core.ExtraTopLevelOperators: SByte ToSByte[T](T) Microsoft.FSharp.Core.ExtraTopLevelOperators: Single ToSingle[T](T) Microsoft.FSharp.Core.ExtraTopLevelOperators: System.Collections.Generic.IDictionary`2[TKey,TValue] CreateDictionary[TKey,TValue](System.Collections.Generic.IEnumerable`1[System.Tuple`2[TKey,TValue]]) +Microsoft.FSharp.Core.ExtraTopLevelOperators: System.Collections.Generic.IReadOnlyDictionary`2[TKey,TValue] CreateReadOnlyDictionary[TKey,TValue](System.Collections.Generic.IEnumerable`1[System.Tuple`2[TKey,TValue]]) Microsoft.FSharp.Core.ExtraTopLevelOperators: System.String ToString() Microsoft.FSharp.Core.ExtraTopLevelOperators: System.Type GetType() Microsoft.FSharp.Core.ExtraTopLevelOperators: T LazyPattern[T](System.Lazy`1[T])