Skip to main content
Version: 0.80.0

Map Module

FScript maps support string, int, bool, and unions whose cases all have no payload.

Use valueType map[keyType] for key annotations, such as string map[int]. The legacy valueType map suffix means string keys. Keys and values can be inferred without annotations.

Key types and inference​

The type before map is the value type. The optional brackets specify the key type.

AnnotationKey typeValue type
int mapstringint
string map[int]intstring
string map[bool]boolstring
string map[Priority]Prioritystring

int map[string] is also accepted and displayed as int map. Prefer the shorter form for string keys. Map types compose with other postfix types: int list map[bool] maps booleans to integer lists, and string map[int] list is a list of integer-keyed maps.

type Priority = | Low | High

let names = { ["owner"] = "Ada" }
let byId = { [0] = "toto"; [1] = "titi" }
let byFlag = Map.ofList [(true, "enabled"); (false, "disabled")]
let byPriority = Map.empty |> Map.add Low "later" |> Map.add High "now"

let getZero m = m |> Map.tryGet 0
let first = byId |> getZero |> Option.defaultValue "missing"

getZero infers 'v map[int] -> 'v option: the lookup constrains the key type to int, while the value type remains polymorphic. The same helper can read maps of strings or integers. Function arguments do not need annotations for this inference.

All keys in a map must share one type, and all values must share one type. Map.fold, Map.filter, and Map.iter receive keys of the map's actual key type. Map.map and Map.choose preserve that key type. Empty maps acquire their key and value types from use.

An unresolved numeric key is constrained to int. This does not constrain numeric map values:

let make k = { [k + k] = "value" } // k is inferred as int
let numericValues v = { [true] = v + v }
let floatValues = numericValues 2.5

When the collection type is unknown, xs[0] infers a list argument. Use Map.tryGet 0 to establish an integer-keyed map without an annotation.

Native map access​

FormSignatureDescription
values[key]'v map['key] -> 'key -> 'v optionOptional lookup by the map key type. Missing keys return None.
let maybeA = { ["a"] = 1 }["a"]

Map.empty : 'v map['key]​

Empty map.

let m = Map.empty

Map.tryGet : 'key -> 'v map['key] -> 'v option​

Looks up a key.

let m = { ["a"] = 1 }
let maybeA = m |> Map.tryGet "a"

Map.containsKey : 'key -> 'v map['key] -> bool​

Checks key existence.

let hasA = { ["a"] = 1 } |> Map.containsKey "a"

Map.add : 'key -> 'v -> 'v map['key] -> 'v map['key]​

Adds or replaces a key value.

let updated = Map.add "port" 8080 Map.empty

Map.ofList : ('key * 'v) list -> 'v map['key]​

Builds a map from key-value tuples.

let m = [("a", 1); ("b", 2)] |> Map.ofList

Map.fold : ('state -> 'key -> 'v -> 'state) -> 'state -> 'v map['key] -> 'state​

Folds over key-value entries.

let total =
{ ["a"] = 1; ["b"] = 2 }
|> Map.fold (fun acc _ value -> acc + value) 0

Map.count : 'v map['key] -> int​

Returns entry count.

let n = { ["a"] = 1; ["b"] = 2 } |> Map.count

Map.filter : ('key -> 'v -> bool) -> 'v map['key] -> 'v map['key]​

Keeps entries that satisfy predicate.

let small =
{ ["a"] = 1; ["b"] = 10 }
|> Map.filter (fun _ v -> v < 5)

Map.choose : ('key -> 'v -> 'u option) -> 'v map['key] -> 'u map['key]​

Transforms entries and drops None results.

let parsed =
{ ["a"] = "1"; ["b"] = "x" }
|> Map.choose (fun _ v -> Int.tryParse v)

Map.map : ('v -> 'u) -> 'v map['key] -> 'u map['key]​

Transforms values.

let doubled = { ["x"] = 2 } |> Map.map (fun v -> v * 2)

Map.iter : ('key -> 'v -> unit) -> 'v map['key] -> unit​

Iterates entries for side effects.

{ ["x"] = 2; ["y"] = 3 } |> Map.iter (fun k v -> Console.writeLine $"{k}={v}")

Map.remove : 'key -> 'v map['key] -> 'v map['key]​

Removes a key.

let m = { ["x"] = 2 } |> Map.remove "x"

Key restrictions​

Float, record, list, tuple, and option keys are unsupported. Mixing key types in one map is also invalid. A union key type must have no payload in any of its cases, even if the map uses only a payload-free case.

// Invalid: float key
// let badFloat = { [1.5] = "one" }

// Invalid: mixed key types
// let mixed = { [0] = "zero"; [true] = "yes" }

type Result = | Empty | Value of int
// Invalid: Result has a payload-bearing case
// let badUnion = { [Empty] = "empty" }

Json.serialize encodes string, integer, and boolean keys as JSON object property names. It returns None for maps containing union keys. The F# type provider supports string-keyed maps only.