bash - lazy completion evaluation.
Tags: lazy, eval, bash, shell
Published: 2019-05-31 10:08:04

---
title: bash - lazy completion evaluation.
published: true
description: Lazy evaluation --- load fast, go fast, use only when needed.
tags: lazy,eval,bash,shell
---

# lazy completion evaluation in bash

tl;dr: loading completions lazily can help with terminal init speed, reduce memory usage (if only slightly), and use expensive functions (eg generators) once only.

lazy evaluation comes with a lot of positives-- less memory, less parsing, less evaluation, and you only pay for what you use. So, what can we do with it in bash?

well, here's a stupid (well, requires some jumps) means of setting it up:

```bash
function __lazyfunc {
  if [[ -z "$(type -t $1)" || $LAZYFUNC_FORCE_REDEFINE ]]; then
    local fn="$1"
    shift
    eval "$fn () { unset -f $fn ; eval \"\$( $@ )\" ; $fn \$@ ; }"
  fi
}
```

so what does it do?

* only define if not declared. If you rerun `. .bashrc` on this, it doesn't override the values.
* (locally) take the first argument, shift it off the front
* evaluate a string defining a function

that's it

---

of course, there are some downsides--

- most declarations are not intended to be done like this
- it takes some editing to rewrite the available source

for example, with this, rather than just

```bash
eval "$(pandoc --bash-completion)"
```

it now takes 

```bash
__lazyfunc _pandoc pandoc --bash-completion
```

and it gets worse for others. eg, nvm:

```bash
export NVM_DIR="$HOME/.nvm"
__lazyfunc __nvm 'cat $NVM_DIR/bash_completion'
__lazyfunc nvm 'cat $NVM_DIR/nvm.sh'
complete -o default -F __nvm nvm
```

cargo and rustup?:

```bash
__lazyfunc _rustup rustup completions bash rustup
__lazyfunc _cargo 'cat $(rustc --print sysroot)/etc/bash_completion.d/cargo'
complete -F _rustup -o bashdefault -o default rustup
complete -F _cargo cargo
```

## in use

using the above settings, with a few moments of loading (and adding `time` before eval call),

```shell
$ nvm a
real	0m0.007s
user	0m0.006s
sys	0m0.001s
lias ^C
$ cargo a
real	0m0.142s
user	0m0.089s
sys	0m0.042s
dd ^C
$ rustup 
real	0m0.023s
user	0m0.012s
sys	0m0.011s

completions     dump-testament  man             show            -v
component       -h              override        target          -V
default         --help          run             toolchain       --verbose
doc             help            self            uninstall       --version
docs            install         set             update          which
$ rustup ^C
$ pandoc 
real	0m1.221s
user	0m0.302s
sys	0m0.202s

$ pandoc
$ npm 
real	0m0.954s
user	0m0.535s
sys	0m0.351s
v12.2.0

real	0m2.186s
user	0m1.153s
sys	0m1.058s
$ node

real	0m0.993s
user	0m0.524s
sys	0m0.404s
Welcome to Node.js v12.2.0.
Type ".help" for more information.
> 
```

(node and npm are loaded through nvm and have their own macros, so this takes a bit longer than usual)

```bash
function node {
        unset -f node npm
        local which=`nvm which | tail -n 1`
        NODE_ICU_DATA="$(dirname "$(dirname "$which")")/lib/node_modules/full-icu"
        PATH="$PATH:$(dirname "$which")"
        $which $@
}
function npm {
        >&2 node -v
        npm $@
}
__lazyfunc _npm_completion npm completion
complete -F _npm_completion npm
```

now, what good is it?

+ on a computer with slow disk access, this greatly decreases time between starting and having access to a terminal
+ on a phone (termux), this also decreases time between startup

of course, there are some downsides

- initiation of a function can add time to initial completion
- this initial setup can be annoying

now, I doubt very much that I'm the first to post about it; but it is worth the time.

# investigation

as you can guess, this means it hot-swaps the functions. so, what's the difference?

```shell
$ type _cargo
_cargo is a function
```

```bash
_cargo () 
{ 
    unset -f _cargo;
    eval "$( cat $(rustc --print sysroot)/etc/bash_completion.d/cargo )";
    _cargo $@
}
```

```shell
$ type __nvm
__nvm is a function
```

```bash
__nvm () 
{ 
    unset -f __nvm;
    eval "$( cat $NVM_DIR/bash_completion )";
    __nvm $@
}
```

```shell
$ nvm alias ^C
$ type __nvm
__nvm is a function
```

```bash
__nvm () 
{ 
    declare previous_word;
    previous_word="${COMP_WORDS[COMP_CWORD - 1]}";
    case "${previous_word}" in 
        use | run | exec | ls | list | uninstall)
            __nvm_installed_nodes
        ;;
        alias | unalias)
            __nvm_alias
        ;;
        *)
            __nvm_commands
        ;;
    esac;
    return 0
}
```