Shell – Is there something like closures for zsh

functionshellzsh

I just decided to try zsh (through oh-my-zsh), and am now playing with precmd to emulate a two-line prompt that has right prompts in more than just the last line.

So I clone the default theme, and inspired by this post (that I'm using to learn a lot too), i do somehting like this (I'll add colors later):

function precmd {
    local cwd="${(%):-[%~]}"
    local who_where="${(%):-%n@%m}"
    local git_info=${(%)$(git_prompt_info)}
    local right_prompt="     $git_info [$who_where]"
    local left_prompt="${(r:(($COLUMNS - ${#${right_prompt}})):: :)cwd}"

    echo "$left_prompt$right_prompt"
}

And it works. But I can't help but wonder: is zsh defining all those variables every time precmd is called?

I've been googling for closures, scope and namespacing in relation to zsh, looking to attach the local vars as data to precmd, so it doesn't need to redefine the variables every time, but I have found nothing. Is there some way to do what I'm trying, or should I just drop it?

As a side note, and only if it is related, what does "to have a function loaded" mean?

Best Answer

Zsh doesn't have anything like closures or packages or namespaces. Zsh lacks a bunch of things required to have true closures:

  • Functions aren't first class. You can't pass functions around as arguments to other functions, and functions can't return other functions. (You can pass the name of a function to call, but that's not the same as passing the function itself).

  • You can't have nested functions. All functions in zsh are global. You must prefix your function names to avoid conflicts. Note especially that functions will shadow external programs with the same name. If you have a function called ls, it will be called instead of the program ls. This can be useful, except if you do it by accident.

  • Variables are dynamically scoped, not statically scoped like in most modern languages. Even if you could have nested functions, inner functions wouldn't close over the local variables of outer functions in the way you would normally expect. You couldn't use them to make modules the way people do in, say, Javascript.

  • Zsh does have anonymous functions, but without any of these other things they're not useful for much.

So basically, the best you can do is to prefix all your functions and global variables.

I'll also point out that you should do define your precmd like this:

% autoload -Uz add-zsh-hook
% add-zsh-hook precmd my_precmd_function

add-zsh-hook lets you hook your function into precmd without it overwriting any other functions that might also want to hook precmd.

What it means to have a function loaded is a separate question. Zsh has an autoloading feature that only loads functions from disk when they're actually called. When you do autoload -Uz foobar, that makes the function named foobar available to call. When you actually call foobar, that loads the definition from disk.