Skip to content

# Collapsing Functions

## What is a "Collapsing Function"?

A collapsing function is a function whose behavior changes depending upon the circumstances under which it's run. Function collapsing is useful when you find yourself repeatedly checking a variable whose value never changes.

## How do I make a function collapse?

Function collapsing requires some static feature in the environment. A common example is a script that gives the user the option of having "verbose" output.

    #!/bin/bash

    [[ $1 = -v || $1 = --verbose ]] && verbose=1

    chatter() {
      if [[ $verbose ]]; then
        chatter() {
          echo "$@"
        }
        chatter "$@"
      else
        chatter() {
          :
        }
      fi
    }

    echo "Waiting for 10 seconds."
    for i in {1..10}; do
      chatter "$i"
      sleep 1
    done

## How does it work?

The first time you run chatter(), the function redefines itself based on the value of verbose. Thereafter, chatter doesn't check `$verbose`, it simply is. Further calls to the function reflect its collapsed nature. If verbose is unset, chatter will echo nothing, with no extra effort from the developer.

## More examples

FIXME

Add more examples!

    # Somewhat more portable find -executable
    # FIXME/UNTESTED (I don't have access to all of the different versions of find.)
    # Usage: find PATH ARGS -- use find like normal, except use -executable instead of
    # various versions of -perm /+ blah blah and hacks
    find() {
      hash find || { echo 'find not found!'; exit 1; }
      # We can be pretty sure "$0" should be executable.
      if [[ $(command find "$0" -executable 2> /dev/null) ]]; then
        unset -f find # We can just use the command find
      elif [[ $(command find "$0" -perm /u+x 2> /dev/null) ]]; then
        find() {
          typeset arg args
          for arg do
            [[ $arg = -executable ]] && args+=(-perm /u+x) || args+=("$arg")
          done
          command find "${args[@]}"
        }
      elif [[ $(command find "$0" -perm +u+x 2> /dev/null) ]]; then
        find() {
          typeset arg args
          for arg do
            [[ $arg = -executable ]] && args+=(-perm +u+x) || args+=("$arg")
          done
          command find "${args[@]}"
        }
      else # Last resort
        find() {
          typeset arg args
          for arg do
            [[ $arg = -executable ]] && args+=(-exec test -x {} \; -print) || args+=("$arg")
          done
          command find "${args[@]}"
        }
      fi
      find "$@"
    }

    #!/bin/bash
    # Using collapsing functions to turn debug messages on/off

    [ "--debug" = "$1" ] && dbg=echo || dbg=:

    # From now on if you use $dbg instead of echo, you can select if messages will be shown

    $dbg "This message will only be displayed if --debug is specified at the command line

November 7, 2024
November 7, 2024

## Comments