F# collection initializer syntax
Solution 1
As Jared says, there is no built-in support for this for arbitrary collections. However, the C# code is just syntactic sugar for Add
method calls, so you could translate it to:
let coll = MyCollectionType()
["One", 1; "Two", 2] |> Seq.iter coll.Add
If you want to get fancy, you could create an inline
definition to streamline this even further:
let inline initCollection s =
let coll = new ^t()
Seq.iter (fun (k,v) -> (^t : (member Add : 'a * 'b -> unit) coll, k, v)) s
coll
let d:System.Collections.Generic.Dictionary<_,_> = initCollection ["One",1; "Two",2]
Solution 2
To elaborate a bit on collection initialization in F#, here are a few examples:
read-only dictionary
dict [ (1, "a"); (2, "b"); (3, "c") ]
seq (IEnumerable<T>)
seq { 0 .. 99 }
list
[1; 2; 3; 4; 5]
set
set [1; 2; 3; 4; 5]
array
[| 1; 2; 3; 4; 5 |]
Solution 3
I don't believe F# has an explicit collection initializer syntax. However it's usually very easy to initialize F# collections. For example
let map = [ ("One", 1); ("Two", 2) ] |> Map.ofSeq
Getting to BCL collections is usually a bit more difficult because they don't always have the handy conversion functions. Dictionary<TKey, TValue>
works though because you can use the LINQ method
let map =
let list = [ ("One", 1); ("Two", 2) ]
System.Linq.Enumerable.ToDictionary(list, fst, snd)
Solution 4
You can use the same :
open System.Collections.Generic
Dictionary<int, string>(dict [ (1, "a"); (2, "b"); (3, "c") ])
Cheers.
Solution 5
The lack of a collection initializer is annoying for some XAML-centric APIs like Workflow 4.0 which rely on collection initializers instead of ctors, e.g.
new Sequence { Activities = { WriteLine { Text = "In the sequence!" } } };
In such cases, imperative .Add() is awkward because the value is conceptually declarative even though it's technically mutable/imperative. However, there's no common base class for the set of all activities which declare an Activities child: the "Activities" member is a pattern and not an interface, so you can't just write a normal helper function which adds children to any activity. Fortunately, F# member constraints come to the rescue.
In order to write this:
Sequence() |> add [Sequence(DisplayName="InnerSeq"); WriteLine(Text = InArgument<_>("In the sequence!"))]
You first need to define an inline helper function called "add":
let inline add (children: Activity seq) =
let inline doAdd (activity: ^Activity) : ^Activity when ^Activity : (member get_Activities : unit -> Activity Collection) =
let collection = (^Activity : (member get_Activities : unit -> Activity Collection) (activity))
for child in children do
collection.Add(child)
activity
doAdd
This still isn't quite as nice as the C# syntax but at least it's still declarative. IMHO this is not so much as a fault with F# as with collection-initializer-centric APIs, but at least F# allows a workaround.
srparish
Updated on July 31, 2022Comments
-
srparish almost 2 years
What is the collection initializer syntax in F#? In C# you can write something like:
new Dictionary<string, int>() { {"One", 1}, {"two", 2}}
How do I do the same thing in F#? I suppose i could roll my own syntax, but seems like there should be a built-in or standard one already.
-
srparish over 13 yearsI'm wanting to initialize other C# collection objects such as MongoDB Bson objects.
-
JaredPar over 13 years@srparish I'm unfamiliar with those objects. Can you link us to an API?
-
srparish over 13 years
-
Stephen Swensen over 13 yearsThe ToDictionary conversion can be simplified:
System.Linq.Enumerable.ToDictionary(list, fst, snd)
-
Daniel over 13 yearsIf an immutable
IDictionary<K, V>
will suffice, you can simply prefix your list withdict
. -
Daniel over 13 yearsA (nit-picky) style preference: I would put this in a
Collection
module and name itinit
, sinceCollection.init [1; 2; 3]
reads nicely. -
kvb over 13 years@Daniel - good point. Also note that in contrast to C#'s syntactic sugar, this function is arity-specific (that is, it only works on
Add
methods taking two arguments) and you'd need to create another function for initializing collections which use a unaryAdd
method (as in your example usage). -
Nicholas W about 13 yearsOf course you mean
Dictionary<int, string>
. That syntax isn't bad though! -
Andrii almost 5 yearsMaybe
dict <| seq { (1, "a"); (2, "b"); (3, "c") }
andset <| seq {1; 2; 3; 4; 5}
are more efficient then?