43

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.

Guy Coder
  • 24,501
  • 8
  • 71
  • 136
srparish
  • 1,708
  • 1
  • 16
  • 22

6 Answers6

83

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 |]
Daniel
  • 47,404
  • 11
  • 101
  • 179
  • 1
    Maybe `dict <| seq { (1, "a"); (2, "b"); (3, "c") }` and `set <| seq {1; 2; 3; 4; 5}` are more efficient then? – Andrii Sep 05 '19 at 12:18
31

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]
kvb
  • 54,864
  • 2
  • 91
  • 133
  • 6
    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. – Daniel Mar 17 '11 at 15:58
  • @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). – kvb Mar 17 '11 at 16:08
15

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)
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
6

You can use the same :

open System.Collections.Generic

Dictionary<int, string>(dict [ (1, "a"); (2, "b"); (3, "c") ])

Cheers.

Jonathan Wilson
  • 4,138
  • 1
  • 24
  • 36
foo
  • 160
  • 4
2

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.

svick
  • 236,525
  • 50
  • 385
  • 514
Max Wilson
  • 21
  • 1
0

Given that the C# collection initializer syntax is syntactic sugar for calling .Add and that implies a mutable collection - I'm not sure you'll see any such syntax in F#. It's initialize all in one go as per JaredPar's answer, or do it manually.

Massif
  • 4,327
  • 23
  • 25