Skip to content

Validating Data with Structs

Defining a #struct is like creating a blueprint. Now, let’s learn how to use that blueprint to check if your data is built correctly. This is done with the :: validation operator, a core part of MON data validation and schema enforcement. This integrated approach to type checking helps prevent data errors in your configuration files, offering a more robust solution than external validation methods often used with JSON.

You use :: after a key to declare, “The following object must match this struct’s blueprint.” The MON compiler will then check the object for you.

Example:

{
// 1. Define the blueprint
User: #struct {
id(Number),
username(String),
is_active(Boolean) = true,
},
// 2. Use the blueprint to validate an instance
alice :: User = {
id: 1,
username: "alice",
},
// This instance would cause an error because `id` is a string, not a number.
// bob :: User = {
// id: "2",
// username: "bob",
// },
}

If validation fails, the compiler will give you a clear error telling you exactly what went wrong (e.g., missing key, wrong value type, or extra key).

The type system can also validate the contents of arrays. This is incredibly powerful for ensuring lists of data are consistent.

Here are the patterns you can use inside a struct definition:

PatternMeaning
[String]An array with exactly one element of type String.
[Number...]An array with zero or more elements, all of type Number.
[String, Number]A “tuple” array with exactly two elements: a String first, then a Number.
[String, Any...]An array with one or more elements. The first must be a String, and the rest can be Any type.
[Boolean..., String]An array with one or more elements. The last must be a String, and all preceding elements must be Boolean.

Example of Collection Validation:

{
LogEntry: #struct {
// The `data` field must be an array starting with a String, followed by any other items.
data([String, Any...]),
},
// This instance is valid.
login_event :: LogEntry = {
data: ["USER_LOGIN", { id: 123, time: "2025-10-26" }],
},
// This instance would fail because it doesn't start with a String.
// invalid_event :: LogEntry = {
// data: [123, "USER_LOGIN"],
// },
}

You are now able to define and validate complex data structures! The final step is to learn how to connect data across different files.