Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you declare the values of a dictionary entry as mutable?

The Google yields plenty of example of adding and deleting entries in an F# dictionary (or other collection). But I don't see examples to the equivalent of

myDict["Key"] = MyValue;

I've tried

myDict.["Key"] <- MyValue

I have also attempted to declare the Dictionary as

Dictionary<string, mutable string>

as well several variants on this. However, I haven't hit on the correct combination yet... if it is actually possible in F#.

Edit: The offending code is:

type Config(?fileName : string) =
    let fileName = defaultArg fileName @"C:\path\myConfigs.ini"

    static let settings =
        dict[ "Setting1", "1";
              "Setting2", "2";
              "Debug",    "0";
              "State",    "Disarray";]

    let settingRegex = new Regex(@"\s*(?<key>([^;#=]*[^;#= ]))\s*=\s*(?<value>([^;#]*[^;# ]))")

    do  File.ReadAllLines(fileName)
        |> Seq.map(fun line -> settingRegex.Match(line))
        |> Seq.filter(fun mtch -> mtch.Success)
        |> Seq.iter(fun mtch -> settings.[mtch.Groups.Item("key").Value] <- mtch.Groups.Item("value").Value)

The error I'm getting is:

System.NotSupportedException: This value may not be mutated
   at [email protected]_Item(K key, V value)
   at <StartupCode$FSI_0036>[email protected](Match mtch)
   at Microsoft.FSharp.Collections.SeqModule.iter[T](FastFunc`2 action, IEnumerable`1 sequence)
   at FSI_0036.Utilities.Config..ctor(Option`1 fileName)
   at <StartupCode$FSI_0041>.$FSI_0041.main@()
stopped due to error
like image 440
telesphore4 Avatar asked Jul 29 '09 21:07

telesphore4


People also ask

How do you make a Python dictionary mutable?

A dictionary is an unordered and mutable Python container that stores mappings of unique keys to values. Dictionaries are written with curly brackets ({}), including key-value pairs separated by commas (,). A colon (:) separates each key from its value.

What way is dictionaries being mutable?

Dictionaries themselves are mutable, so entries can be added, removed, and changed at any time. Note, though, that because entries are accessed by their key, we can't have two entries with the same key.

Are the values in a Python dictionary mutable?

Dictionary is a built-in Python Data Structure that is mutable. It is similar in spirit to List, Set, and Tuples. However, it is not indexed by a sequence of numbers but indexed based on keys and can be understood as associative arrays.

Can dictionary keys be mutable?

Second, a dictionary key must be of a type that is immutable. For example, you can use an integer, float, string, or Boolean as a dictionary key. However, neither a list nor another dictionary can serve as a dictionary key, because lists and dictionaries are mutable.


2 Answers

f# has two common associative data structures:

The one you are most used to, the mutable Dictionary which it inherits that's to it's presence in the BCL and uses a hashtable under the hood.

let dict = new System.Collections.Generic.Dictionary<string,int>()
dict.["everything"] <- 42

The other is known as Map and is, in common functional style, immutable and implemented with binary trees.

Instead of operations that would change a Dictionary, maps provide operations which return a new map which is the result of whatever change was requested. In many cases, under the hood there is no need to make an entirely new copy of the entire map, so those parts that can be shared normally are. For example:

let withDouglasAdams = Map.add "everything" 42 Map.empty

The value withDouglasAdams will remain forever as an association of "everything" to 42. so if you later do:

let soLong = Map.remove "everything" withDouglasAdams

Then the effect of this 'removal' is only visible via the soLong value.

F#'s Map is, as mentioned, implemented as a binary tree. Lookup is therefore O(log n) whereas a (well behaved) dictionary should be O(1). In practice a hash based dictionary will tend to outperform the tree based one in almost all simple (low number of elements, low probability of collision) as such is commonly used. That said the immutable aspect of the Map may allow you to use it in situations where the dictionary would instead require more complex locking or to write more 'elegant' code with fewer side effects and thus it remains a useful alternative.

This is not however the source of your problem. The dict 'operator' returns an explicity immutable IDictionary<K,T> implementation (despite not indicating this in it's documentation).

From fslib-extra-pervasives.fs (note also the use of options on the keys):

let dict l = 
    // Use a dictionary (this requires hashing and equality on the key type)
    // Wrap keys in an Some(_) option in case they are null 
    // (when System.Collections.Generic.Dictionary fails). Sad but true.
    let t = new Dictionary<Option<_>,_>(HashIdentity.Structural)
    for (k,v) in l do 
        t.[Some(k)] <- v
    let d = (t :> IDictionary<_,_>)
    let c = (t :> ICollection<_>)
    let ieg = (t :> IEnumerable<_>)
    let ie = (t :> System.Collections.IEnumerable)
    // Give a read-only view of the dictionary
    { new IDictionary<'key, 'a> with 
            member s.Item 
                with get x = d.[Some(x)]            
                and  set (x,v) = raise (NotSupportedException(
                                            "This value may not be mutated"))
   ...
like image 116
ShuggyCoUk Avatar answered Oct 13 '22 21:10

ShuggyCoUk


What error do you get? I tried the following and it compiles just fine

let map = new System.Collections.Generic.Dictionary<string,int>()
map.["foo"] <- 42

EDIT Verify that this code ran just fine as well .

like image 23
JaredPar Avatar answered Oct 13 '22 19:10

JaredPar