Constraints

Template constraints allow you to specify requirements that template type parameters must satisfy, providing compile-time guarantees that template arguments have the necessary methods or properties.

Benefits

  • Type Safety: Catch errors at compile time instead of link time
  • Better Error Messages: Clear messages about what's missing
  • Self-Documenting: Constraints serve as documentation
  • Interface Enforcement: Ensure types conform to expected interfaces

Constraint Declaration

xxml
[ Constraint <ConstraintName> <T Constrains None> (T a)
    Require (F(ReturnType)(methodName)(*) On a)
]

Syntax Breakdown

  1. [ Constraint <Name> - Start constraint declaration
  2. <T Constrains None> - Template parameters for the constraint
  3. (T a) - Parameter bindings (maps template param T to identifier a)
  4. Require (...) - One or more requirement statements
  5. Close with ]

Requirement Types

Method Requirements

Require that a type has specific methods with particular signatures:

xxml
Require (F(ReturnType)(methodName)(*) On paramName)
  • F - Indicates a function/method requirement
  • First parentheses - Return type
  • Second parentheses - Method name
  • Third parentheses - * for any parameters, or specific parameter types
  • On paramName - Which template parameter this applies to
xxml
// Require a method named "toString" returning String^
Require (F(String^)(toString)(*) On a)

// Require a method named "run" returning Integer^
Require (F(Integer^)(run)(*) On a)

// Require a method with specific parameter types
Require (F(Bool^)(equals)(T^) On a)

// Require a method returning None (void)
Require (F(None)(doSomething)(*) On a)

Constructor Requirements

Require that a type has specific constructors:

xxml
// Default constructor
Require (C(None) On a)

// Constructor taking an Integer
Require (C(Integer^) On a)

// Constructor taking multiple parameters
Require (C(String^, Integer^) On a)

Truth Requirements

Require that a boolean expression is true at compile time:

xxml
// Require that type T is exactly SomeClass
Require (Truth(TypeOf<T>() == TypeOf<SomeClass>()))

Using Constraints

Step 1: Define a Constraint

xxml
[ Constraint <Printable> <T Constrains None> (T a)
    Require (F(String^)(toString)(*) On a)
]

Step 2: Apply to Template Class

xxml
// Single constraint
[ Class <Printer> <T Constrains Printable> Final Extends None

// Single constraint with template arguments (two syntaxes)
[ Class <HashSet> <T Constrains Hashable<T>> Final Extends None
[ Class <HashSet> <T Constrains Hashable@T> Final Extends None

// Multiple constraints with AND semantics (must satisfy ALL)
[ Class <HashMap> <K Constrains (Hashable<K>, Equatable@K)> Final Extends None

// Multiple constraints with OR semantics (must satisfy at least ONE)
[ Class <FlexibleContainer> <T Constrains Printable | Comparable> Final Extends None

Note

Use parentheses with commas for AND semantics (all constraints must be satisfied). Use pipe | for OR semantics (at least one must be satisfied).

Step 3: Instantiate with Valid Types

xxml
// Integer has toString(), so this works
Instantiate Printer<Integer>^ As <intPrinter> = Printer@Integer::Constructor();

// String has toString(), so this works
Instantiate Printer<String>^ As <strPrinter> = Printer@String::Constructor();

Complete Example: Printable Constraint

printable.xxml
[ Constraint <Printable> <T Constrains None> (T a)
    Require (F(String^)(toString)(*) On a)
]

[ Class <Logger> <T Constrains Printable> Final Extends None
    [ Public <>
        Property <items> Types Collections::List<T>^;

        Constructor Parameters() -> {
            Set items = Collections::List@T::Constructor();
        }

        Method <add> Returns None Parameters (Parameter <item> Types T^) Do {
            Run items.add(item);
        }

        Method <logAll> Returns None Parameters () Do {
            For (Integer <i> = 0 .. items.size()) -> {
                Instantiate T^ As <item> = items.get(i);
                Run Console::printLine(item.toString());
            }
        }
    ]
]

Common Constraint Patterns

Comparable

xxml
[ Constraint <Comparable> <T Constrains None> (T a)
    Require (F(Bool^)(lessThan)(T^) On a)
    Require (F(Bool^)(equals)(T^) On a)
]

Sortable

xxml
[ Constraint <Sortable> <T Constrains None> (T a)
    Require (F(Bool^)(lessThan)(T^) On a)
    Require (F(Bool^)(greaterThan)(T^) On a)
    Require (F(Bool^)(equals)(T^) On a)
    Require (F(String^)(toString)(*) On a)
]

Hashable (from STL)

xxml
[ Constraint <Hashable> <T Constrains None> (T a)
    Require (F(NativeType<"int64">^)(hash)(*) On a)
]

Numeric

xxml
[ Constraint <Numeric> <T Constrains None> (T a)
    Require (F(T^)(add)(T^) On a)
    Require (F(T^)(subtract)(T^) On a)
    Require (F(T^)(multiply)(T^) On a)
    Require (F(T^)(divide)(T^) On a)
]

Constraint Syntax Summary

SyntaxMeaning
<T Constrains None>No constraints
<T Constrains Printable>Single constraint
<T Constrains Hashable<T>>With template args (angle brackets)
<T Constrains Hashable@T>With template args (at-sign)
<T Constrains (A, B)>AND semantics (must satisfy all)
<T Constrains A | B>OR semantics (satisfy at least one)

Error Handling

If you try to use a type that doesn't satisfy the constraint, you'll get a clear compile-time error:

text
Error: Type 'MyClass' does not satisfy constraint 'Printable'
Required method: toString

Tip

Add the required method to your class to satisfy the constraint. Ensure the method has the correct name, return type, and parameter types.

Best Practices

  • Use minimal requirements: Only require what you actually need
  • Clear naming: Use descriptive constraint names like Sortable not S
  • Document requirements: Add comments explaining why each requirement exists
  • Test with multiple types: Verify constraints work with various types

Next Steps

Learn about Generics for template programming, or explore Classes to understand how constraints are applied.