std.record

apply_on : forall a b. String -> (a -> a -> b) -> { _ : a } -> { _ : a } -> b

apply_on field f a b is equivalent to f a."${field}" b."${field}" Useful for comparisons.

Examples

std.record.apply_on "name" std.string.compare { name = "Alice", age = 27 } { name = "Bob", age = 23 }
# => 'Lesser

std.record.apply_on "age" std.number.compare { name = "Alice", age = 27 } { name = "Bob", age = 23 }
# => 'Greater

fields : forall a. { _ : a } -> Array String

Returns an array containing the names of all the fields of a record.

Empty optional fields

By default, fields ignores optional fields without definition. An empty optional field won't be listed in the result.

Use std.record.fields_with_opts for a version which doesn't ignore empty optional fields.

Examples

std.record.fields { one = 1, two = 2 }
# => [ "one", "two" ]

std.record.fields { one = 1, two = 2, three_opt | optional }
# => [ "one", "two" ]

fields_with_opts : forall a. { _ : a } -> Array String

Returns an array containing the names of all the fields of a record.

Same as std.record.fields, but doesn't ignore empty optional fields.

Examples

std.record.fields_with_opts { one = 1, two = 2 }
# => [ "one", "two" ]

std.record.fields_with_opts { one = 1, two = 2, three_opt | optional }
# => [ "one", "two", "three_opt" ]

filter : forall a. (String -> a -> Bool) -> { _ : a } -> { _ : a }

filter f r returns a record containing all fields from r for which f returns true. The function f is passed the name and value of each field to make a decision.

Examples

std.record.filter (fun _name x => x % 2 == 0) { even = 2, odd = 3 }
# => { even = 2 }

from_array : forall a. Array { field : String, value : a } -> { _ : a }

Converts an array of key-value pairs into a record. The field names in the input array must be distinct.

Examples

std.record.from_array [
  { field = "hello", value = "world" },
  { field = "foo", value = "bar" }
]
# => { hello = "world", foo = "bar" }

get : forall a. String -> { _ : a } -> a

get | std.contract.unstable.HasField Dyn

Returns the field of a record with the given name.

std.record.get field record is just record."%{field}". The latter form is more idiomatic and should be generally preferred.

However, std.record.get can come in handy when a proper function is expected, typically in a sequence of operations chained with the reverse application operator |>.

Trying to extract a field which doesn't exist will result in a contract error.

Examples

std.record.get "one" { one = 1, two = 2 }
# => 1

{ one = 1, two = 2, string = "three"}
|> std.record.to_array
|> std.array.filter (fun { field, value } => std.is_number value)
|> std.record.from_array
|> std.record.get "two"
# => 2

get_or : forall a. String -> a -> { _ : a } -> a

Returns the field of a record with the given name, or the default value if either there is no such field or if this field doesn't have a definition.

Examples

std.record.get_or "tree" 3 { one = 1, two = 2 }
# => 3

std.record.get_or "one" 11 { one = 1, two = 2 }
# => 1

std.record.get_or "value" "default" { tag = 'Hello, value }
# => "default"

has_field : forall a. String -> { _ : a } -> Bool

Given a string, checks if a record contains a field of that name.

Empty optional fields

By default, has_field ignores optional fields without definition.

Use std.record.has_field_with_opts for a version which doesn't ignore empty optional fields.

Examples

std.record.has_field "hello" { one = 1, two = 2 }
# => false

std.record.has_field "one" { one = 1, two = 2 }
# => true

std.record.has_field "two_opt" { one, two_opt | optional }
# => false

({ one = 1 } | {one, two_opt | optional })
|> std.record.has_field "two_opt"
# => false

has_field_with_opts : forall a. String -> { _ : a } -> Bool

Given a string, checks if a record contains a field of that name.

Same as std.record.has_field, but doesn't ignore empty optional fields.

Examples

std.record.has_field_with_opts "hello" { one = 1, two = 2 }
# => false

std.record.has_field_with_opts "one" { one = 1, two = 2 }
# => true

std.record.has_field_with_opts "two_opt" { one, two_opt | optional }
# => true

({ one = 1 } | {one, two_opt | optional })
|> std.record.has_field_with_opts "two_opt"
# => true

insert : forall a. String -> a -> { _ : a } -> { _ : a }

Inserts a new field into a record. insert doesn't mutate the original record but returns a new one instead.

Preconditions

The field must not exist in the initial record, otherwise insert will fail. If the field might already exist, use std.record.update instead.

Empty optional fields

By default, insert ignores optional fields without definition. The precondition above doesn't apply to empty optional fields, and insert will silently erase an existing empty optional field.

Use std.record.insert_with_opts for a version which doesn't ignore empty optional fields.

Examples

std.record.insert "foo" 5 { bar = "bar" }
# => { foo = 5, bar = "bar" }

{}
|> std.record.insert "file.txt" "data/text"
|> std.record.insert "length" (10*1000)
# => {"file.txt" = "data/text", "length" = 10000}

std.record.insert "already_there_opt" 0 {already_there_opt | optional}
# => {already_there_opt = 0}

std.record.insert "already_there" 0 {already_there = 1}
# => error

insert_with_opts : forall a. String -> a -> { _ : a } -> { _ : a }

Inserts a new field into a record. insert_with_opts doesn't mutate the original record but returns a new one instead.

Same as std.record.insert, but doesn't ignore empty optional fields.

Preconditions

The field must not exist in the initial record, otherwise insert will fail. If the field might already exist, use std.record.update instead.

Examples

std.record.insert_with_opts "foo" 5 { bar = "bar" }
# => { foo = 5, bar = "bar" }

{}
|> std.record.insert_with_opts "file.txt" "data/text"
|> std.record.insert_with_opts "length" (10*1000)
# => {"file.txt" = "data/text", "length" = 10000}

std.record.insert_with_opts "already_there_optional" 0 {already_there | optional}
# => { already_there_optional = 0 }

std.record.insert_with_opts "already_there" 0 {already_there = 1}
# => error

is_empty : forall a. { _ : a } -> Bool

Checks whether a record is empty.

Examples

std.record.is_empty {}
# => true

std.record.is_empty { foo = 1 }
# => false

length : forall a. { _ : a } -> Number

Returns the number of fields in a record. This count doesn't include fields both marked as optional and without a definition.

Because of the need to filter empty optional fields, the cost of length is linear in the size of the record.


map : forall a b. (String -> a -> b) -> { _ : a } -> { _ : b }

Maps a function over every field of a record. The function is passed the name and value of each field.

Examples

std.record.map (fun s x => s) { hi = 2 }
# => { hi = "hi" }

std.record.map (fun s x => x + 1) { hello = 1, world = 2 }
# => { hello = 2, world = 3 }

map_values : forall a b. (a -> b) -> { _ : a } -> { _ : b }

Maps a function over the values of all the fields of a record. map_values f is the same as std.record.map (fun _field => f).

Examples

std.record.map_values (fun x => x + 1) { hi = 2 }
# => { hi = 3 }

std.record.map_values (fun x => x + 1) { hello = 1, world = 2 }
# => { hello = 2, world = 3 }

merge_all : Array ({ _ : Dyn }) -> { _ : Dyn }

Merges an array of records.

Examples

std.record.merge_all [ { foo = 1 }, { bar = 2 } ]
# => { foo = 1, bar = 2 }

remove : forall a. String -> { _ : a } -> { _ : a }

Removes a field from a record. remove doesn't mutate the original record but returns a new one instead.

Preconditions

The field to remove must be present in the record, or remove will fail.

Empty optional fields

By default, remove ignores optional fields. The precondition above doesn't apply to an empty optional field and remove will fail with a missing field error when trying to remove an empty optional field.

Use std.record.remove_with_opts for a version which doesn't ignore empty optional fields.

Examples

std.record.remove "foo" { foo = "foo", bar = "bar" }
# => { bar = "bar" }

std.record.remove "foo_opt" {foo_opt | optional}
# => error

std.record.remove "foo" { bar = "bar" }
# => error

remove_with_opts : forall a. String -> { _ : a } -> { _ : a }

Removes a field from a record. remove doesn't mutate the original record but returns a new one instead.

Same as std.record.remove, but doesn't ignore empty optional fields.

Preconditions

The field to remove must be present in the record, or remove will fail.

Examples

std.record.remove_with_opts "foo" { foo = "foo", bar = "bar" }
# => { bar = "bar" }

std.record.remove_with_opts "foo_opt" {foo_opt | optional}
# => {}

std.record.remove_with_opts "foo" { bar = "bar" }
# => error

to_array : forall a. { _ : a } -> Array { field : String, value : a }

Converts a record to an array of key-value pairs.

Examples

std.record.to_array { hello = "world", foo = "bar" }
# => [ { field = "hello", value = "world" }, { field = "foo", value = "bar" } ]

update : forall a. String -> a -> { _ : a } -> { _ : a }

Updates a field of a record with a new value. update doesn't mutate the original record but returns a new one instead. If the field to update is absent from the given record, update adds it.

Empty optional fields

update works the same way for empty optional fields and other fields. update guarantees that it can always replace a field with a new value, no matter the nature of the field.

Examples

std.record.update "foo" 5 { foo = "foo", bar = "bar" }
# => { foo = 5, bar = "bar" }

std.record.update "foo" 5 { bar = "bar" }
# => { foo = 5, bar = "bar" }

std.record.update "foo_opt" 5 {foo_opt | optional}
# => {foo_opt = 5}

Overriding

As opposed to overriding a value with the merge operator &, update will only change the specified field and won't automatically update the other fields which depend on it:

{ foo = bar + 1, bar | default = 0 } & { bar = 1 }
# => { foo = 2, bar = 1 }

std.record.update "bar" 1 {foo = bar + 1, bar | default = 0 }
# => { foo = 1, bar = 1 }

values : forall a. { _ : a } -> Array a

Returns an array containing the values of all the fields of a record.

Examples

std.record.values { one = 1, world = "world" }
# => [ 1, "world" ]