How does method overloading work compared to function dispatching?
← Functions and Methods · Ref: Q600
EK9 has two distinct mechanisms for calling different code based on types: method overloading (compile-time resolution) and dispatching (runtime resolution).
METHOD OVERLOADING
Classes can have multiple methods with the same name but different parameter types. The compiler resolves which overload to call at COMPILE time using cost-based matching: exact type match (cost 0), subtype (cost 1), promotion like Integer to Float (cost 2), and Any match (cost 3). The lowest cost wins. If two overloads tie, the compiler reports an ambiguity error.
DISPATCHER KEYWORD
The 'dispatcher' keyword enables RUNTIME type-based dispatch. Mark a method as dispatcher and provide overloaded implementations:
format() as dispatcher -> item as Any <- rtn as String: $item format() -> item as Integer <- rtn as String: "Int: " + $item
When called, the runtime checks the actual type of the argument and dispatches to the most specific overload.
KEY DIFFERENCE
With overloading, if you have a variable typed as 'Any' that holds an Integer, the compiler selects the 'Any' overload. With dispatcher, the runtime selects the 'Integer' overload because it examines the actual type.
FUNCTION DISPATCHING
Standalone functions cannot share names in the same module. For function-level dispatch, use the 'as dispatcher' modifier. Dispatchers are limited to one or two parameters, must have the same parameter count across overloads, and must share the same purity.
WHEN TO CHOOSE
Use method overloading when the caller knows the exact type at compile time. Use dispatcher when processing heterogeneous collections or when the runtime type matters. Dispatcher eliminates the need for the visitor pattern.
See Q60 for function dispatching basics. See Q105 for method dispatch in class hierarchies. See Q255 for cost-based method resolution details. See Q596 for function vs method distinction.
Example
defines module qa.functionsAndMethods.overloadingVsDispatch defines class //Class with overloaded methods (compile-time resolution) Formatter formatItem() as pure -> item as String <- rtn as String: "String: " + item formatItem() as pure -> item as Integer <- rtn as String: "Integer: " + $item formatItem() as pure -> item as Float <- rtn as String: "Float: " + $item //Class using dispatcher (runtime resolution) RuntimeFormatter describe() as dispatcher -> item as Any <- rtn as String: "Any type" describe() -> item as Integer <- rtn as String: "Int: " + $item describe() -> item as String <- rtn as String: "Str: " + item defines program OverloadingVsDispatchDemo() stdout <- Stdout() formatter <- Formatter() // === COMPILE-TIME OVERLOADING === // Compiler selects overload based on declared type stdout.println(formatter.formatItem("hello")) stdout.println(formatter.formatItem(42)) stdout.println(formatter.formatItem(3.14)) // === RUNTIME DISPATCHING === // Dispatcher selects overload based on runtime type runtimeFmt <- RuntimeFormatter() //Direct calls: same as overloading stdout.println(runtimeFmt.describe(42)) stdout.println(runtimeFmt.describe("hello")) //Heterogeneous list: runtime dispatch shines here items <- [1, 2.5, "text"] for item in items stdout.println(runtimeFmt.describe(item))
Common mistakes
E50001 — A pure method cannot call non-pure methods like stdout.println(). Pure methods must have no side effects. See ek9 -h E50001 for details.
Incorrect:
formatItem() as pure -> item as String <- rtn as String: "String: " + item stdout.println(item)
Correct:
formatItem() as pure -> item as String <- rtn as String: "String: " + item
Other ways to ask this
- What is the difference between method overloading and dispatcher in EK9?
- When should I use dispatcher instead of overloaded methods?
- How does EK9 resolve overloaded method calls?
Coming from another language?
Java: method overloading resolved at compile time, visitor pattern for runtime dispatch, instanceof checks as workaround, no multiple dispatch. Python: no method overloading (last definition wins), functools.singledispatch for single parameter. JavaScript: no overloading, manual type checking with typeof. Rust: no method overloading, trait dispatch for polymorphism, match on enums. Go: no overloading, type switch for runtime dispatch. Kotlin: method overloading like Java, when with is for runtime checks, no dispatcher. C#: method overloading at compile time, dynamic keyword for runtime. Julia: multiple dispatch built into language, most similar to EK9 dispatcher. EK9: method overloading with cost-based compile-time resolution, 'dispatcher' keyword for runtime type-based dispatch, eliminates visitor pattern.
Keywords: type, compile-time, overloading, runtime, dispatcher, visitor, matching, dispatch, resolution, parameter, method, sealed, handler, cost