ValueLabelSet Module
Bidirectional mappings between integer values and human-readable labels.
The ValueLabelSet module provides functions for creating and manipulating value label sets, which map integer codes to descriptive strings. This is particularly useful for working with survey data, coded variables, and STATA-style value labels.
Common patterns
import ValueLabelSet
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
gender |> ValueLabelSet.label 1 -- Just "Male"
Type-safe value labels
Define integer-to-label mappings directly in enum types using ValueLabel, then extract them with fromType:
import ValueLabelSet
type Gender
= Male (ValueLabel 1 "Male")
| Female (ValueLabel 2 "Female")
let labels = ValueLabelSet.fromType Gender
labels |> ValueLabelSet.label 1 -- Just "Male"
fromType is a compiler intrinsic that reads the mappings at compile time. It requires all variants to have exactly one ValueLabel field, and integer values must be unique.
Enum variant labels
Labels can also be enum variants with display labels:
type AgeGroup = Young "<30" | Middle "30-60" | Old ">60"
ValueLabelSet.fromList [(1, AgeGroup::Young), (2, AgeGroup::Middle)]
Functions
Create
ValueLabelSet.fromList
[(Int, a)] -> ValueLabelSet
Create a ValueLabelSet from a list of (value, label) pairs. Labels can be strings or enum variants (using display label or variant name).
import ValueLabelSet
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
ValueLabelSet.label 1 gender -- Just "Male"Try itSee also: ValueLabelSet.empty, ValueLabelSet.insert
ValueLabelSet.fromType
TypeName -> ValueLabelSet
Create a ValueLabelSet from a ValueLabel enum type at compile time.
The compiler reads the ValueLabel fields from the enum definition and builds the mapping statically. All variants must have exactly one ValueLabel field, and integer values must be unique.
import ValueLabelSet
enum Gender
= Male (ValueLabel 1 "Male")
| Female (ValueLabel 2 "Female")
let labels = ValueLabelSet.fromType Gender
labels |> ValueLabelSet.label 1 -- Just "Male"Try itNotes: This is a compiler intrinsic — the type is resolved at compile time, not at runtime. The type name is passed unquoted (not as a string).
See also: ValueLabelSet.fromList
ValueLabelSet.empty
ValueLabelSet
Create an empty ValueLabelSet.
import ValueLabelSet
let vls = ValueLabelSet.fromList []
|> ValueLabelSet.insert 1 "Yes"
|> ValueLabelSet.insert 0 "No"
ValueLabelSet.size vls -- 2Try itSee also: ValueLabelSet.fromList
ValueLabelSet.insert
Int -> String -> ValueLabelSet -> ValueLabelSet
Insert a single value-label pair into the set.
import ValueLabelSet
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
|> ValueLabelSet.insert 9 "Unknown"
ValueLabelSet.size gender -- 3Try itSee also: ValueLabelSet.fromList
Lookup
ValueLabelSet.label
Int -> ValueLabelSet -> Maybe String
Get the label for a value.
import ValueLabelSet
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
let found = ValueLabelSet.label 1 gender -- Just "Male"
let missing = ValueLabelSet.label 9 gender -- Nothing
missingTry itSee also: ValueLabelSet.value
ValueLabelSet.value
String -> ValueLabelSet -> Maybe Int
Get the value for a label (reverse lookup).
import ValueLabelSet
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
let found = ValueLabelSet.value "Female" gender -- Just 2
let missing = ValueLabelSet.value "Other" gender -- Nothing
missingTry itSee also: ValueLabelSet.label
Query
ValueLabelSet.size
ValueLabelSet -> Int
Get the number of labels.
import ValueLabelSet
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
ValueLabelSet.size gender -- 2Try itSee also: ValueLabelSet.isEmpty
ValueLabelSet.isEmpty
ValueLabelSet -> Bool
Check if the label set is empty.
import ValueLabelSet
let a = ValueLabelSet.isEmpty (ValueLabelSet.fromList []) -- True
let b = ValueLabelSet.isEmpty (ValueLabelSet.fromList [(1, "Yes")]) -- False
bTry itSee also: ValueLabelSet.size
ValueLabelSet.hasValue
Int -> ValueLabelSet -> Bool
Check if a value has a label.
import ValueLabelSet
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
let found = ValueLabelSet.hasValue 1 gender -- True
let missing = ValueLabelSet.hasValue 9 gender -- False
missingTry itSee also: ValueLabelSet.hasLabel
ValueLabelSet.hasLabel
String -> ValueLabelSet -> Bool
Check if a label exists.
import ValueLabelSet
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
let found = ValueLabelSet.hasLabel "Male" gender -- True
let missing = ValueLabelSet.hasLabel "Other" gender -- False
missingTry itSee also: ValueLabelSet.hasValue
ValueLabelSet.coverage
[Int] -> ValueLabelSet -> { labeled: [Int], unlabeled: [Int], unused: [Int] }
Check coverage of values against the label set. Returns which values have labels, which don't, and which labels have no matching values.
import ValueLabelSet
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female"), (9, "Unknown")]
let seen_values = [1, 2, 2, 1, 3]
let report = ValueLabelSet.coverage seen_values gender
-- report.labeled = [1, 2] (values with labels)
-- report.unlabeled = [3] (values without labels)
-- report.unused = [9] (labels with no matching value in data)Try itValueLabelSet.describe
ValueLabelSet -> String
Return a formatted table showing all value-label mappings.
import ValueLabelSet
import IO
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female"), (9, "Unknown")]
IO.println (ValueLabelSet.describe gender)
-- Value | Label
-- ------+----------
-- 1 | Male
-- 2 | Female
-- 9 | UnknownTry itConvert
ValueLabelSet.toList
ValueLabelSet -> [(Int, String)]
Convert to a list of (value, label) pairs.
import ValueLabelSet
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
ValueLabelSet.toList gender -- [(1, "Male"), (2, "Female")]Try itSee also: ValueLabelSet.fromList
ValueLabelSet.values
ValueLabelSet -> [Int]
Get all values in the label set.
import ValueLabelSet
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
ValueLabelSet.values gender -- [1, 2]Try itSee also: ValueLabelSet.labels
ValueLabelSet.labels
ValueLabelSet -> [String]
Get all labels in the label set.
import ValueLabelSet
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
ValueLabelSet.labels gender -- ["Male", "Female"]Try itSee also: ValueLabelSet.values
Transform
ValueLabelSet.merge
ValueLabelSet -> ValueLabelSet -> ValueLabelSet
Merge two label sets (first takes priority on conflicts).
import ValueLabelSet
let a = ValueLabelSet.fromList [(1, "Yes"), (2, "No")]
let b = ValueLabelSet.fromList [(2, "Nope"), (3, "Maybe")]
let merged = ValueLabelSet.merge a b
-- 1 = "Yes", 2 = "No" (a wins conflict), 3 = "Maybe"
ValueLabelSet.size merged -- 3Try itValueLabelSet.filter
[Int] -> ValueLabelSet -> ValueLabelSet
Filter to only include values in the given list.
import ValueLabelSet
let status = ValueLabelSet.fromList [(1, "Active"), (2, "Inactive"), (3, "Pending")]
let active_only = ValueLabelSet.filter [1, 3] status
ValueLabelSet.size active_only -- 2Try itValueLabelSet.remap
[(Int, Int)] -> ValueLabelSet -> ValueLabelSet
Remap values according to a list of (old, new) pairs.
import ValueLabelSet
-- Recode 1/2 to 0/1
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
let recoded = ValueLabelSet.remap [(1, 0), (2, 1)] gender
ValueLabelSet.label 0 recoded -- Just "Male"Try itValueLabelSet.invert
ValueLabelSet -> Maybe ValueLabelSet
Invert the label set (swap values and labels). Returns Nothing if labels aren't numeric.
import ValueLabelSet
-- Labels are numeric strings, so invert works
let vls = ValueLabelSet.fromList [(1, "2"), (2, "1")]
ValueLabelSet.invert vls -- Just (ValueLabelSet with 1 -> "2", 2 -> "1" swapped)
-- Labels are not numeric, so invert returns Nothing
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
ValueLabelSet.invert gender -- NothingTry it