Write a higher-order function that takes a predicate and counts matching items in a list.

← Functions and Methods · Ref: Q1269

EK9 uses abstract functions as the equivalent of functional interfaces. Define the abstract function with its signature, then create concrete implementations as DYNAMIC FUNCTIONS using the syntax '() is AbstractName as function'.

ABSTRACT FUNCTION (the predicate type)

  StringCheck as abstract
    -> text as String
    <- rtn as Boolean?

DYNAMIC FUNCTION (the implementation)

  isLong <- () is StringCheck as function
    rtn: length text > LONG_THRESHOLD

Note: the dynamic function body uses the SAME parameter names as the abstract ('text', 'rtn'). Parameters are inherited, not redeclared.

HIGHER-ORDER FUNCTION (accepts the predicate)

  countMatching()
    -> items as List of String, predicate as StringCheck
    <- rtn as Integer: 0
    for item in items
      if predicate(item)
        rtn++

Call the predicate with normal function-call syntax: 'predicate(item)' returns Boolean.

See Q709 for built-in Predicate/Assessor types. See Q714 for Comparator. See Q1238 for strategy pattern using this approach.

Example

defines module qa.functionsandmethods.predicatehigherorder

  defines constant

    LONG_THRESHOLD <- 5

  defines function

    StringCheck as abstract
      -> text as String
      <- rtn as Boolean?

    countMatching()
      ->
        items as List of String
        predicate as StringCheck
      <- rtn as Integer: 0
      for item in items
        if item?
          matched <- predicate(item)
          if matched
            rtn++

  defines program

    PredicateDemo()
      stdout <- Stdout()

      words <- List() of String
      words += "hi"
      words += "EK9-Language"
      words += "compiler"
      words += "go"
      words += "abstract-function"

      isLong <- () is StringCheck as function
        rtn: length text > LONG_THRESHOLD

      longCount <- countMatching(words, isLong)
      stdout.println(`Words longer than ${LONG_THRESHOLD}: ${longCount}`)

Common mistakes

E11064 — Bare literal values in comparisons are not allowed — extract to a named constant. 'LONG_THRESHOLD <- 5' in a defines constant block. See ek9 -h E11064 for details.

Incorrect:

        rtn: length text > 5

Correct:

        rtn: length text > LONG_THRESHOLD
Other ways to ask this
  • How do I pass a function as a parameter in EK9?
  • Show me an abstract function used as a predicate with a dynamic function implementation.
  • Write a countMatching function that accepts a StringCheck predicate and a List of String.
  • In Java I'd use Predicate<String> — what is the EK9 pattern for higher-order functions?

Coming from another language?

Java: Predicate<String> isLong = s -> s.length() > 5; items.stream().filter(isLong).count(). Kotlin: val isLong: (String) -> Boolean = { it.length > 5 }; items.count(isLong). Python: is_long = lambda s: len(s) > 5; sum(1 for s in items if is_long(s)). EK9: abstract function + dynamic function '() is StringCheck as function' + higher-order parameter passing.

Keywords: dynamic function, function parameter, countMatching, predicate, abstract function, higher-order, delegate