Verbs Become Nouns

In human language, verbs describe actions and nouns describe things. "Run" is a verb. "Runner" is a noun. We can transform one into the other: "to run" becomes "the act of running."

Programming has the same duality. Functions are verbs---they describe actions, transformations, processes. But in many languages, functions can also be treated as nouns---as things that can be stored, passed around, and manipulated.

This chapter explores that transformation: when verbs become nouns.

Functions as Values

Consider a simple function:

javascript
    function double(x):
        return x * 2

We can call this function: double(5) returns 10.

Calling a function: data goes in, result comes out.

But we can also refer to the function itself, without calling it:

    my_function = double      // not double(...)

Here, my_function now holds the function double---not the result of calling it, but the function itself. We can call it later:

python
    my_function = double
    result = my_function(5)   // result is 10

The function has become a value. A verb has become a noun.

Why Does This Matter?

This might seem like a curiosity. Why would we want to store a function in a variable?

The power emerges when we realize we can:

  1. Choose which function to use at runtime
  2. Pass functions to other functions
  3. Return functions from functions

Let's explore each.

Choosing at Runtime

Imagine our chess game has different AI opponents:

javascript
    function random_move(state):
        return pick_random(get_legal_moves(state))
    
    function greedy_move(state):
        return pick_best_capture(get_legal_moves(state))
    
    function defensive_move(state):
        return pick_safest(get_legal_moves(state))

Notice something: each strategy is built by chaining functions together. The output of one becomes the input of the next. And the whole chain is itself a function:

A function containing functions: random_move chains get_legal_moves and pick_random. Input enters, flows through the internal chain, output emerges.

A player might choose their opponent's style:

    if difficulty == "easy":
        opponent_strategy = random_move
    else if difficulty == "medium":
        opponent_strategy = greedy_move
    else:
        opponent_strategy = defensive_move
    
    // Later, in the game loop:
    opponent_move = opponent_strategy(current_state)

The variable opponent_strategy holds a function. Which function depends on the player's choice. The rest of the code just calls opponent_strategy---it doesn't need to know which specific function it is.

Passing Functions

We can pass functions as arguments to other functions. This is called using a callback---we're saying "call this function back when you need it."

Passing a function as a value: double itself (the box, not its result) goes into map.
javascript
    function play_game(initial_state, white_player, black_player):
        state = initial_state
        while not is_game_over(state):
            if state.turn == white:
                move = white_player(state)
            else:
                move = black_player(state)
            state = apply_move(state, move)
        return state

Now we can play any combination:

python
    // Human vs AI
    play_game(start, human_input, greedy_move)
    
    // AI vs AI  
    play_game(start, random_move, defensive_move)
    
    // Human vs Human
    play_game(start, human_input, human_input)

The play_game function is parameterized by behavior. It doesn't just take data---it takes functions that determine how decisions are made.

Returning Functions

Functions can also return other functions. This creates functions dynamically.

javascript
    function make_multiplier(factor):
        function multiply(x):
            return x * factor
        return multiply
python
    double = make_multiplier(2)
    triple = make_multiplier(3)
    
    double(5)    // returns 10
    triple(5)    // returns 15
A function factory: data goes in, a function comes out.

What does double look like inside? It's a closure---a function paired with captured state:

Closures as boxes: both contain the same code (x * factor), but each remembers a different factor.

We've created a function factory---a function that produces other functions. Each produced function has its own "personality" determined by the factor.

Functions Remember: Closures

In the make_multiplier example, something subtle happened. The inner function multiply refers to factor---but factor is a parameter of the outer function.

When we call make_multiplier(2), the value 2 is bound to factor. The returned function "remembers" this value, even after make_multiplier has finished executing.

This is a closure: a function bundled together with references to its surrounding state.

javascript
    function make_counter():
        count = 0
        function increment():
            count = count + 1
            return count
        return increment
    
    counter_a = make_counter()
    counter_b = make_counter()
    
    counter_a()   // returns 1
    counter_a()   // returns 2
    counter_b()   // returns 1 (separate count!)

Each call to make_counter creates a new, independent counter. The closures carry their own private state.

Two closures, two worlds: each counter is a box containing both behavior (the increment function) and private state (its own count).

Anonymous Functions

Sometimes we need a small function just once, for a specific purpose. Naming it seems like overkill.

Many languages allow anonymous functions---functions without names:

python
    // Named function
    function double(x):
        return x * 2
    
    // Anonymous function (same thing, no name)
    (x) => x * 2

We can use anonymous functions directly:

    numbers = [1, 2, 3, 4, 5]
    
    doubled = map(numbers, (x) => x * 2)
    // doubled is [2, 4, 6, 8, 10]

The anonymous function (x) => x * 2 is created just for this one use. It has no name---it's a "verb" used immediately, not stored as a "noun."

The Strange Loop

There's something almost paradoxical about functions being values. A function is an action, a process, a transformation. Yet we can hold it still, examine it, pass it around like an object.

It's like being able to hold "the act of running" in your hand. Not a runner, not the result of running---but running itself, frozen as a thing.

This creates opportunities for self-reference. A function can:

javascript
    function repeat(action, times):
        if times == 0:
            return "done"
        else:
            action()
            return repeat(action, times - 1)
  • Return itself
  • Receive a copy of itself as an argument
  • Create modified versions of itself

Here, repeat receives a function (action) and calls itself with modified arguments. The function processes functions and refers to itself.

Notice something profound: this code isn't just a program. It's a definition of what repeating means. We could express the same idea in natural language:

To repeat something zero times is to do nothing. To repeat something $n$ times is to do it once, then repeat it $n-1$ times.

This is abstract logic thinking. The definition works regardless of what the action is. It captures the pure essence of repetition---the pattern itself, separated from any specific instance.

Tracing Execution Through Substitution

Let's trace what happens when we call repeat(echo("hello"), 4). Rather than "executing" in the procedural sense, we can think of each step as substitution---replacing a function call with its equivalent form:

python
    repeat(echo("hello"), 4)
    = echo("hello"); repeat(echo("hello"), 3)
    = echo("hello"); echo("hello"); repeat(echo("hello"), 2)
    = echo("hello"); echo("hello"); echo("hello"); repeat(echo("hello"), 1)
    = echo("hello"); echo("hello"); echo("hello"); echo("hello"); repeat(echo("hello"), 0)
    = echo("hello"); echo("hello"); echo("hello"); echo("hello"); "done"

    -> "hello", "hello", "hello", "hello"

This resembles algebraic manipulation in mathematics! We aren't "running" anything---we're rewriting the program into a more verbose but equivalent form.

Functions as values opens a door to a new style of programming. Instead of telling the computer how to do something step by step, we can describe what we want by combining functions.

In the next chapter, we explore this further: functions that receive other functions as arguments, allowing us to delegate the "how" while focusing on the "what."