Skip to main content

Generics

Mux supports generics with type parameters and interface bounds, similar to Go and Rust.

Generic Functions

Functions can be generic over type parameters:

generic_identity.mux
Loading...

Type Constraints

Use the is keyword to constrain type parameters to specific interfaces:

generic_constraints.mux
Loading...

Multiple Type Parameters

multiple_type_params.mux
Loading...

Multiple Bounds (AND Semantics)

Type parameters can have multiple constraints:

multiple_bounds.mux
Loading...

Generic Classes

Classes can be generic over type parameters:

generic_classes.mux
Loading...

Generic Classes with Multiple Type Parameters

generic_pair.mux
Loading...

Built-in Interfaces

Mux provides built-in interfaces for common operations:

InterfaceDescription
StringableTypes that can be converted to string (via .to_string() method)
EquatableTypes that support == and != operators
ComparableTypes that support <, <=, >, >= operators
HashableTypes that can be used as keys in sets and maps
ErrorTypes that can be used as result<T, E> errors (via .message() method)

Operator Mapping

  • a + b - Works for types with native support (int, float, string, list, map, set)
  • a > b - Works for types with native support (int, float, string, char)
  • a == b - Works for all types

Primitives and Interfaces

TypeImplements
intStringable, Equatable, Comparable, Hashable
floatStringable, Equatable, Comparable, Hashable
stringStringable, Equatable, Comparable, Hashable, Error
boolStringable, Equatable, Hashable
charStringable, Equatable, Comparable, Hashable

result<T, E> requires E to implement Error.

Implementing Interfaces for Custom Types

Custom types can implement interfaces to work with generic functions:

custom_equatable.mux
Loading...

Generic Functions with Collections

generic_collections_funcs.mux
Loading...

Monomorphization

Mux uses compile-time monomorphization for generics - specialized code is generated for each type instantiation:

monomorphization.mux
Loading...

How Monomorphization Works

  1. Type inference: Determine concrete types from function arguments
  2. Name generation: Create unique identifier: FunctionName$$Type1$$Type2$$
  3. Type substitution: Replace type parameters with concrete types
  4. Code generation: Emit specialized function body
  5. Caching: Store generated methods to avoid regeneration

Benefits

  • Zero runtime cost: No boxing, no vtables, no type checks
  • Static dispatch: Methods resolved at compile time
  • LLVM optimization: Each specialization can be fully optimized

Tradeoff

  • Increased intermediate code size and compilation time: One copy per type combination

Type Inference in Generics

Type parameters can often be inferred from context:

type_inference_generics.mux
Loading...

However, when ambiguous, explicit types are required:

explicit_generic_types.mux
Loading...

Generic Enums

Enums can be generic:

generic_enums.mux
Loading...

See Enums and Error Handling for more details.

Constraints and Bounds

Single Constraint

single_constraint.mux
Loading...

Multiple Constraints (AND)

multiple_constraints.mux
Loading...

Type T must implement all specified interfaces.

Best Practices

  1. Use type constraints - Specify what operations are needed
  2. Prefer interface bounds over concrete types - More flexible
  3. Leverage monomorphization - No runtime overhead for generics
  4. Use descriptive type parameter names - T, U, K, V for simple cases
  5. Explicit types when ambiguous - Helps readability
  6. Keep generic functions simple - Complex logic harder to debug
  7. Document constraints - Make requirements clear

See Also