Skip to content
Merged
154 changes: 100 additions & 54 deletions src/fsharp/FSharp.Core/fslib-extra-pervasives.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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>) =
[<DebuggerDisplay("Count = {Count}")>]
[<DebuggerTypeProxy(typedefof<DictDebugView<_,_,_>>)>]
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<KeyValuePair<'Key, 'T>> 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<KeyValuePair<'Key, 'T>> with
member __.Count = t.Count

interface IEnumerable<KeyValuePair<'Key, 'T>> 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>) =
[<DebuggerBrowsable(DebuggerBrowsableState.RootHidden)>]
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<KeyValuePair<'Key, 'T>> 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<KeyValuePair<'Key, 'T>> 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
Expand All @@ -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

[<CompiledName("CreateDictionary")>]
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 :> _

[<CompiledName("CreateReadOnlyDictionary")>]
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
Expand Down
4 changes: 4 additions & 0 deletions src/fsharp/FSharp.Core/fslib-extra-pervasives.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ module ExtraTopLevelOperators =
[<CompiledName("CreateDictionary")>]
val dict : keyValuePairs:seq<'Key * 'Value> -> System.Collections.Generic.IDictionary<'Key,'Value> when 'Key : equality

/// <summary>Builds a read-only lookup table from a sequence of key/value pairs. The key objects are indexed using generic hashing and equality.</summary>
[<CompiledName("CreateReadOnlyDictionary")>]
val readOnlyDict : keyValuePairs:seq<'Key * 'Value> -> System.Collections.Generic.IReadOnlyDictionary<'Key,'Value> when 'Key : equality

/// <summary>Builds a 2D array from a sequence of sequences of elements.</summary>
[<CompiledName("CreateArray2D")>]
val array2D : rows:seq<#seq<'T>> -> 'T[,]
Expand Down
5 changes: 3 additions & 2 deletions tests/FSharp.Core.UnitTests/FSharp.Core.Unittests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>3</WarningLevel>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetDotnetProfile)' != 'coreclr' ">
<ItemGroup Condition=" '$(TargetDotnetProfile)' != 'coreclr' ">
<!-- need full name and SpecificVersion = true in order to convince msbuild to allow this reference when targeting portable47 -->
<Reference Include="nunit.framework, Version=$(NUnitFullVersion), Culture=neutral, PublicKeyToken=2638cd05610744eb">
<SpecificVersion>true</SpecificVersion>
Expand Down Expand Up @@ -105,6 +105,7 @@
<Compile Include="FSharp.Core\Microsoft.FSharp.Core\OptionModule.fs" />
<Compile Include="FSharp.Core\Microsoft.FSharp.Core\PrintfTests.fs" />
<Compile Include="FSharp.Core\Microsoft.FSharp.Core\ResultTests.fs" />
<Compile Include="FSharp.Core\Microsoft.FSharp.Core\ExtraTopLevelOperatorsTests.fs" />
<Compile Include="FSharp.Core\Microsoft.FSharp.Control\Cancellation.fs" />
<Compile Include="FSharp.Core\Microsoft.FSharp.Control\AsyncType.fs" />
<Compile Include="FSharp.Core\Microsoft.FSharp.Control\LazyType.fs" />
Expand All @@ -125,4 +126,4 @@
</CopyAndSubstituteText>
</ItemGroup>
<Import Project="$(FSharpSourcesRoot)\FSharpSource.targets" />
</Project>
</Project>
Loading