Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# Generic IDictionary extension failed

Tags:

f#

f#-3.0

When I try to extend IDictionary with the following code

type Generic.IDictionary<'TKey, 'TValue> with
    member this.GetOrAdd (key:'TKey, fun':'TKey -> 'TValue) =
        match this.ContainsKey(key) with
        | true -> this.Item(key)
        | false -> 
            let val_ = fun' key
            this.Add(key, val_)
            val_

let dd = dict[(1,2); (3,4)]
let f = fun x -> 2*x
let d5 = dd.GetOrAdd(5, f)

I got the following error in runtime.

System.NotSupportedException: Exception of type >'System.NotSupportedException' was thrown. at Microsoft.FSharp.Core.ExtraTopLevelOperators.dictValueType@98.System->Collections-Generic-IDictionary2-Add(TKey key, T value) at FSI_0011.IDictionary2.GetOrAdd[TKey,TValue](IDictionary2 this, >TKey key, FSharpFunc2 fun') in >D:\BaiduYunDownload\DroiEtl\Droi.MyToPG\Util.Sync.fs:line 259 at .$FSI_0018.main@() in >D:\BaiduYunDownload\DroiEtl\Droi.MyToPG\Util.Sync.fs:line 264 Stopped due to error

But the compiler doesn't complain when building... Please help me...

like image 479
Anibal Yeh Avatar asked Feb 06 '23 15:02

Anibal Yeh


1 Answers

dict is documented to return a read-only IDictionary<_,_> – you then call .Add on it, and the backing class rightly throws an exception.

Create a real Dictionary instance instead and you'll see it works as expected:

open System.Collections.Generic

type IDictionary<'TKey, 'TValue> with
    member this.GetOrAdd (key:'TKey, fun':'TKey -> 'TValue) =
        match this.ContainsKey key with
          | true  -> this.Item key
          | false -> let val' = fun' key
                     this.Add (key, val')
                     val'

let dd =
    let d = Dictionary()
    d.Add (1, 2)
    d.Add (3, 4)
    d

printfn "%A" dd
dd.GetOrAdd (5, (fun x -> 2 * x)) |> printfn "%A :: %d" dd
dd.GetOrAdd (5, (fun x -> 9 * x)) |> printfn "%A :: %d" dd

Output:

seq [[1, 2]; [3, 4]]
seq [[1, 2]; [3, 4]; [5, 10]] :: 10
seq [[1, 2]; [3, 4]; [5, 10]] :: 10

Online Demo

Implementation-wise, @JoelMueller's suggestion is an obvious improvement:

type IDictionary<'TKey, 'TValue> with
    member this.GetOrAdd (key:'TKey, fun':'TKey -> 'TValue) =
        match this.TryGetValue key with
          | true, val' -> val'
          | false, _   -> let val' = fun' key
                          this.Add (key, val')
                          val'
like image 62
ildjarn Avatar answered Feb 15 '23 05:02

ildjarn