F# collection initializer syntax

12,123

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.

Share:
12,123
srparish
Author by

srparish

Updated on July 31, 2022

Comments

  • srparish
    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
    srparish over 13 years
    I'm wanting to initialize other C# collection objects such as MongoDB Bson objects.
  • JaredPar
    JaredPar over 13 years
    @srparish I'm unfamiliar with those objects. Can you link us to an API?
  • srparish
    srparish over 13 years
  • Stephen Swensen
    Stephen Swensen over 13 years
    The ToDictionary conversion can be simplified: System.Linq.Enumerable.ToDictionary(list, fst, snd)
  • Daniel
    Daniel over 13 years
    If an immutable IDictionary<K, V> will suffice, you can simply prefix your list with dict.
  • Daniel
    Daniel over 13 years
    A (nit-picky) style preference: I would put this in a Collection module and name it init, since Collection.init [1; 2; 3] reads nicely.
  • kvb
    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 unary Add method (as in your example usage).
  • Nicholas W
    Nicholas W about 13 years
    Of course you mean Dictionary<int, string>. That syntax isn't bad though!
  • Andrii
    Andrii almost 5 years
    Maybe dict <| seq { (1, "a"); (2, "b"); (3, "c") } and set <| seq {1; 2; 3; 4; 5} are more efficient then?