What happens with two-parameter dispatchers and type validation?

← Dispatcher Validation · Ref: Q619

EK9 supports true multiple dispatch: dispatchers can dispatch on two or more parameters simultaneously. The type hierarchy validation (E05210) applies to EACH dispatchable parameter independently.

MULTIPLE DISPATCH

A two-parameter dispatcher examines the runtime type of both arguments to select the most specific handler:

  intersect() as dispatcher
    -> s1 as Shape, s2 as Shape
  intersect()
    -> s1 as Circle, s2 as Circle
  intersect()
    -> s1 as Circle, s2 as Rectangle

TYPE CHECKING PER PARAMETER

Each handler parameter must be a subtype of the corresponding dispatcher entry parameter. If the first entry parameter is Shape, all handlers' first parameters must extend Shape. The same applies to the second parameter.

COST CALCULATION

The total dispatch cost is the sum of costs for each parameter. A handler with Circle (cost 0.05) + Rectangle (cost 0.05) has total cost 0.10. Exact matches on both parameters (cost 0.00 + 0.00) always win.

AMBIGUITY WITH MULTIPLE PARAMETERS

Ambiguity (E05230) can occur when two handlers have the same total cost. For example, handler(Circle, Rectangle) and handler(Rectangle, Circle) both cost 0.10 when called with a type that matches both.

TRUE MULTIPLE DISPATCH

This is true multiple dispatch, a feature only Julia and Common Lisp natively support among mainstream languages. EK9 provides it with compile-time validation.

See Q60 for dispatcher fundamentals. See Q614 for single-parameter hierarchy. See Q616 for ambiguity detection.

Example

defines module qa.dispatchervalidation.twoparam

  defines class

    Shape as abstract
      name() as pure abstract
        <- rtn as String?
      default operator ?

    Circle extends Shape
      override name() as pure
        <- rtn as String: "circle"
      default operator ?

    Rectangle extends Shape
      override name() as pure
        <- rtn as String: "rectangle"
      default operator ?

    //CORRECT: Two-parameter dispatcher with all types in hierarchy
    CollisionDetector
      collides() as dispatcher
        ->
          s1 as Shape
          s2 as Shape
        <- rtn as String: `${s1.name()} vs ${s2.name()}`

      collides()
        ->
          s1 as Circle
          s2 as Circle
        <- rtn as String: "circle-circle collision"

      collides()
        ->
          s1 as Circle
          s2 as Rectangle
        <- rtn as String: "circle-rectangle collision"

      collides()
        ->
          s1 as Rectangle
          s2 as Rectangle
        <- rtn as String: "rectangle-rectangle collision"

  defines function

    testTwoParamDispatch()
      detector <- CollisionDetector()

      circle <- Circle()
      rect <- Rectangle()

      require detector.collides(circle, circle) == "circle-circle collision"
      require detector.collides(circle, rect) == "circle-rectangle collision"
      require detector.collides(rect, rect) == "rectangle-rectangle collision"

Common mistakes

E05210 — Each parameter in a multi-parameter dispatcher must be a subtype of the corresponding entry parameter. The entry takes Shape for both, so String is invalid for either parameter. See ek9 -h E05210 for details.

Incorrect:

collides()
        ->
          s1 as Circle
          s2 as String

Correct:

collides()
        ->
          s1 as Circle
          s2 as Rectangle

E05220 — All dispatcher handlers must return the same type as the entry method. The entry returns String, so a handler returning Integer is incompatible. See ek9 -h E05220 for details.

Incorrect:

collides()
        ->
          s1 as Circle
          s2 as Circle
        <- rtn as Integer: 0

Correct:

collides()
        ->
          s1 as Circle
          s2 as Circle
        <- rtn as String: "circle-circle collision"
Other ways to ask this
  • Can EK9 dispatchers dispatch on multiple parameters?
  • How does multi-parameter dispatch work in EK9?
  • Does E05210 apply to each parameter in a dispatcher?

Coming from another language?

Java: no multiple dispatch (single dispatch via virtual methods). Python: functools.singledispatch (single parameter only). Julia: built-in multiple dispatch (most similar to EK9). Common Lisp: CLOS multi-methods. Rust: no dispatch mechanism. Kotlin: no multiple dispatch. EK9: true multiple dispatch with compile-time E05210 validation per parameter.

Keywords: ambiguity, E05230, visitor, multi, two, dispatch, multiple, E05210, validate, parameter, sealed, true, handler, cost