Skip to content

Instantly share code, notes, and snippets.

@fkurz
Last active February 17, 2021 14:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fkurz/cf00cf22acb6f40879c47e34074f991a to your computer and use it in GitHub Desktop.
Save fkurz/cf00cf22acb6f40879c47e34074f991a to your computer and use it in GitHub Desktop.
Haskell Shell Scripting with GHCI and Turtle

Shell scripting with Haskell

Problem

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—or similar languages—as a shell scripting language?

Using GHCI for System Programing

Setup

  1. Install Haskell Stack

    brew install haskell-stack
  2. Install turtle using Stack

    stack install turtle
  3. 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
  4. Run Haskell Shell and have fun 🙌

    $ hsh -e git -e latexmk -e typora
    [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. 🚀

The Good

There are a couple cool aspects of Haskell/GHCI that are being used to provide a proper shell feeling:

  1. monads: most Turtle functions like dir and echo are 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
  2. overloaded strings:

    E.g. echo has type echo :: MonadIO io => Line -> io () (i.e. expects Line not String) which can however use the overloaded string literal syntax

    λ> echo "Hi!"
  3. printing of return values: types like FilePath are printable and GHCI prints every value that is entered so we can immediately see results of commands

    λ> dir <- pwd
    λ> dir
    FilePath "/Users/friedrichk/.hsh"

The Turtle tutorial on Hackage has more examples. Be sure to check it out.

The Bad

  • No Tab-Completion for commands/paths
  • No Syntax-Highlighting

Bonus Round

Launching GHCI Directly in Your Terminal Emulator

In a terminal emulator like iTerm, we can start hsh in every new window/tab in order to get a complete Haskell based Terminal feeling.

Future Work

ptghci adds useful features like syntax highlighting and tab completion and might be a good extension.


SOURCES

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