Skip to content

Instantly share code, notes, and snippets.

@smac89
Last active August 4, 2023 05:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save smac89/4b85bd3f9fb902439c0e67e36272832e to your computer and use it in GitHub Desktop.
Save smac89/4b85bd3f9fb902439c0e67e36272832e to your computer and use it in GitHub Desktop.
ZSH lazy loading #zsh #lazy

The following is a function I use in my .zshrc file to achieve some form of lazy evaluation.

local function lazy_load() {
    local -xr thunk="$(cat)"
    # (u) removes duplicates
    local -xr triggers=(${(u)@})
    
    # Only if length of triggers is greater than zero
    # otherwise the function will immediately execute.
    # (X) reports errors if any
    if [ ${(X)#triggers} -gt 0 ]; then
        eval " ${(@)triggers}() {
            trigger=\"\$0\"
            unfunction ${(@)triggers}
            ${thunk}
            if type \$trigger > /dev/null; then
                \$trigger \${@}
            fi
        }"
    fi
}

Before I explain how the function works, let me just show you how to use it:

# lazy load sdkman
lazy_load 'sdk' 'kotlin' 'gradle' 'mvn' <<- 'EOF'
	#THIS MUST BE AT THE END OF THE FILE FOR SDKMAN TO WORK!!!
	export SDKMAN_DIR="${HOME}/.sdkman"
	[ -s "$SDKMAN_DIR/bin/sdkman-init.sh" ] && source "$SDKMAN_DIR/bin/sdkman-init.sh"
EOF

Now when I open a new shell and type which mvn, I get the following:

mvn () {
	trigger="$0" 
	unfunction sdk kotlin gradle mvn
	export SDKMAN_DIR="${HOME}/.sdkman" 
	[ -s "$SDKMAN_DIR/bin/sdkman-init.sh" ] && source "$SDKMAN_DIR/bin/sdkman-init.sh"
	if type $trigger > /dev/null
	then
		$trigger ${@}
	fi
}

After I type mvn --help and do another which mvn, I get:

/home/chigozirim/.sdkman/candidates/maven/current/bin/mvn

Cool! 🚀


How it works

The function works in the following way:

  • Read the contents of the shell command you would have had to execute when the .zshrc is read. This is read from stdin
  • Saves this thunk as a local variable within the function
  • Use eval to create a new function based on the triggers specified (zsh allows us to create a function with multiple names; how convienient!)
    • Note: The triggers should really just be commands that correspond to what is being loaded, but you can use anything you want.
  • Any of the triggers/commands can now be used to cause the loading of the saved thunk
  • After being triggered, the generated function is forgotten and deletes the other triggers
  • The generated function will now test if the trigger is an actual command, and try to execute it.

Profit! 💸


The inspiration to do this came from reading this blog post:

https://frederic-hemberger.de/notes/shell/speed-up-initial-zsh-startup-with-lazy-loading/

Hope this helps someone ❤️

P.S.

At the end of your .zshrc, please add:

unfunction lazy_load

This is mostly for security 👮 due to the use of eval 👿.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment