Shell scripting is kinda broken:
- Shell scripting languages differ or are interpreted with subtle differences
- Higher level language concepts are typically missing
Can Haskell be a good replacement for Bash鈥攐r similar languages鈥攁s a shell scripting language?
-
Install Haskell Stack
brew install haskell-stack
-
Install turtle using Stack
stack install turtle
-
Define functions to setup/init/extend Haskell shell and create an alias hsh
hsh_setup() { local hsh_home="${HOME}/.hsh" mkdir -p ${hsh_home} printf "[INFO] Creating hsh config" printf ':set -XOverloadedStrings\n:set prompt "位> "\n:set +m\nimport Turtle\nimport Prelude hiding (FilePath)' > ${hsh_home}/.ghci printf "\n[INFO] hsh setup done" } hsh_extend() { local hsh_home="${HOME}/.hsh" \ command=${1} printf "\n[INFO] Creating function for command %-25s" "${command}" printf "\n%1\$s = \\\cl -> shell (\"%1\$s \" <> cl) empty\n%1\$s' = \\\cl -> shell (\"%1\$s \" <> cl)" ${command} >> ${hsh_home}/.ghci } hsh_init() ( local hsh_home="${HOME}/.hsh" hsh_setup while getopts "e:" option; do case ${option} in e) hsh_extend ${OPTARG} esac done cd ${hsh_home} stack ghci ) alias hsh=hsh_init
-
Run Haskell Shell and have fun 馃檶
$ hsh -e git [INFO] Creating hsh config [INFO] hsh setup done [INFO] Creating function for command git [INFO] Creating function for command latexmk [INFO] Creating function for command typora Note: No local targets specified, so a plain ghci will be started with no package hiding or package options. You are using snapshot: lts-17.2 If you want to use package hiding and options, then you can try one of the following: * If you want to start a different project configuration than /Users/main/.stack/global-project/stack.yaml, then you can use stack init to create a new stack.yaml for the packages in the current directory. * If you want to use the project configuration at /Users/main/.stack/global-project/stack.yaml, then you can add to its 'packages' field. Configuring GHCi with the following packages: GHCi, version 8.10.3: https://www.haskell.org/ghc/ :? for help Loaded GHCi configuration from /Users/main/.hsh/.ghci Loaded GHCi configuration from /private/var/folders/yz/b655kl71057db1lhwxhc0rt00000gs/T/haskell-stack-ghci/2a3bbd58/ghci-script
In the ghci REPL we can now use the Turtle library to do system programming similar to what we awould do in Bash. 馃殌
There are two very cool aspects of Haskell/GHCI that are being used here:
-
monads: most Turtle functions like
dirandechoare monadic位> :t pwd pwd :: MonadIO io => io FilePath
and GHCI executes code within an IO monad; that means we can write in do-notation style and read/write from/to input/output
位> dir <- pwd 位> dir FilePath "/Users/friedrichk/.hsh"
-
overloaded strings:
- E.g.
echohas typeecho :: MonadIO io => Line -> io ()(i.e. expectsLinenotString) which can however use the overloaded string literal syntax
位> echo "Hi!"
- E.g.
-
printing of return values: return of echo is something that is printable and GHCI therefore prints it nicely
位> echo "Hi!" Hi!
The Turtle tutorial on Hackage has more examples. Be sure to check it out.
- No Tab-Completion for commands/paths
- No Syntax-Highlighting
In a terminal emulator like iTerm, we can start hsh in every new window/tab in order to get a complete Haskell based Shell feeling.
ptghci adds useful features like syntax highlighting and tab completion and might be a good extension.