Why cannot I use a function type as a generic constraint?

← Generics · Ref: Q647

EK9 does not allow function types as constraints in 'constrain by'. Only class types and traits can be used as constraints. Function types have a different dispatch model and cannot serve as type bounds.

PROHIBITED PATTERN

You cannot write:

  Handler of type T constrain by SomeFunction  // ERROR E06080

Function types cannot be used with 'constrain by'.

WHY PROHIBITED

Function types in EK9 define signatures (input types and return type). They are not class hierarchies with methods. Constraining T to a function would mean T must be callable, but EK9's generic system works with method access, not call-ability.

CORRECT ALTERNATIVES

Use a trait or abstract class as the constraint:

  Handler of type T constrain by Processable

Define a trait with the methods you need, then constrain by that trait.

FUNCTION PARAMETERS

If you need to pass functions to generics, use function-typed parameters:

  process()
    -> mapper as Mapper

Where Mapper is a defined function type, passed as a parameter rather than a constraint.

See Q195 for constrain by with classes. See Q646 for inference limits. See Q649 for generic function implementation.

Example

defines module qa.genericsdeep.nofunctionconstraint

  defines trait

    <?-
      Correct approach: define a trait with the methods you need.
      Then constrain by the trait, not by a function type.
    -?>
    Describable
      description() as pure abstract
        <- rtn as String?

  defines class

    Planet with trait of Describable
      planetName as String?

      default private Planet() as pure

      Planet() as pure
        -> n as String
        planetName :=? String(n)

      override description() as pure
        <- rtn as String: String(planetName)

      override operator ? as pure
        <- rtn as Boolean: planetName?

    Star with trait of Describable
      starName as String?

      default private Star() as pure

      Star() as pure
        -> n as String
        starName :=? String(n)

      override description() as pure
        <- rtn as String: String(starName)

      override operator ? as pure
        <- rtn as Boolean: starName?

  defines function

    <?-
      A function type - cannot be used as a constraint.
    -?>
    CheckFunction()
      -> arg0 as String
      <- rtn as Boolean: arg0?

    <?-
      Correct: constrain by a trait, not a function.
      The trait provides the methods needed inside the generic.
    -?>
    describeItem() of type T constrain by Describable as open
      -> item as T
      <- rtn as String: item.description()

  defines program

    NoFunctionConstraintDemo()
      stdout <- Stdout()

      planet <- Planet("Mars")
      planetDesc <- describeItem(planet)
      stdout.println(`Planet: ${planetDesc}`)

      star <- Star("Polaris")
      starDesc <- describeItem(star)
      stdout.println(`Star: ${starDesc}`)

Common mistakes

E50010 — The constraining type must be a resolvable class or trait. Using a name that does not exist triggers a type-not-resolved error. See ek9 -h E50010 for details.

Incorrect:

constrain by NoSuchTrait as open

Correct:

constrain by Describable as open

E06080 — Generic types cannot be constrained by function types. Use a trait or class as the constraint. See ek9 -h E06080 for details.

Incorrect:

    describeItem() of type T constrain by CheckFunction as open

Correct:

    describeItem() of type T constrain by Describable as open
Other ways to ask this
  • What is E06080 constrained functions not supported?
  • Can I constrain a generic type parameter to a function type?
  • Why does EK9 prohibit function types in constrain by?

Coming from another language?

Java: can use functional interfaces as bounds '<T extends Function<S,R>>'. C++: concepts can constrain to callable. Rust: trait bounds with Fn/FnMut/FnOnce. Go: interfaces can include method signatures (callable). Kotlin: can bound by function types. EK9: function types excluded from 'constrain by', use traits instead.

Keywords: bound, constrain, trait, prohibited, constraint, constant, type-parameter, E06080, function, generic