Skip to content

Instantly share code, notes, and snippets.

@fkurz
Last active February 17, 2021 14:40
Show Gist options
  • Select an option

  • Save fkurz/cf00cf22acb6f40879c47e34074f991a to your computer and use it in GitHub Desktop.

Select an option

Save fkurz/cf00cf22acb6f40879c47e34074f991a to your computer and use it in GitHub Desktop.

Revisions

  1. fkurz revised this gist Feb 17, 2021. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion haskell-shell.md
    Original file line number Diff line number Diff line change
    @@ -149,4 +149,5 @@ In a terminal emulator like [iTerm](https://iterm2.com/), we can start _hsh_ in
    ## SOURCES
    * https://downloads.haskell.org/~ghc/7.6.3/docs/html/users_guide/ghci-dot-files.html
    * https://downloads.haskell.org/~ghc/7.6.3/docs/html/users_guide/ghci-dot-files.html
    * https://bobkonf.de/2017/slides/thoma.pdf
  2. fkurz revised this gist Feb 13, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion haskell-shell.md
    Original file line number Diff line number Diff line change
    @@ -138,7 +138,7 @@ The [Turtle tutorial on Hackage](https://hackage.haskell.org/package/turtle-1.5.
    ### Launching GHCI Directly in Your Terminal Emulator
    In a terminal emulator like [iTerm](https://iterm2.com/), we can start _hsh_ in every new window/tab in order to get a complete Haskell based Shell feeling.
    In a terminal emulator like [iTerm](https://iterm2.com/), we can start _hsh_ in every new window/tab in order to get a complete Haskell based Terminal feeling.
    ## Future Work
  3. fkurz revised this gist Feb 13, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion haskell-shell.md
    Original file line number Diff line number Diff line change
    @@ -113,7 +113,7 @@ There are a couple cool aspects of Haskell/GHCI that are being used to provide a
    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
    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
    ```haskell
    λ> echo "Hi!"
  4. fkurz revised this gist Feb 13, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion haskell-shell.md
    Original file line number Diff line number Diff line change
    @@ -96,7 +96,7 @@ In the ghci REPL we can now use the Turtle library to do system programming simi
    ### The Good
    There are two very cool aspects of Haskell/GHCI that are being used here:
    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
  5. fkurz revised this gist Feb 13, 2021. 1 changed file with 5 additions and 6 deletions.
    11 changes: 5 additions & 6 deletions haskell-shell.md
    Original file line number Diff line number Diff line change
    @@ -67,7 +67,7 @@ Can Haskell be a good replacement for Bash—or similar languages—as a shell s
    4. Run Haskell Shell and have fun :raised_hands:
    ```bash
    $ hsh -e git
    $ hsh -e git -e latexmk -e typora
    [INFO] Creating hsh config
    [INFO] hsh setup done
    [INFO] Creating function for command git
    @@ -109,8 +109,6 @@ There are two very cool aspects of Haskell/GHCI that are being used here:
    ```haskell
    λ> dir <- pwd
    λ> dir
    FilePath "/Users/main/.hsh"
    ```
    2. overloaded strings:
    @@ -121,11 +119,12 @@ There are two very cool aspects of Haskell/GHCI that are being used here:
    λ> echo "Hi!"
    ```
    3. printing of return values: return of echo is something that is printable and GHCI therefore prints it nicely
    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
    ```haskell
    λ> echo "Hi!"
    Hi!
    λ> dir <- pwd
    λ> dir
    FilePath "/Users/friedrichk/.hsh"
    ```
    The [Turtle tutorial on Hackage](https://hackage.haskell.org/package/turtle-1.5.21/docs/Turtle-Tutorial.html) has more examples. Be sure to check it out.
  6. fkurz revised this gist Feb 13, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion haskell-shell.md
    Original file line number Diff line number Diff line change
    @@ -110,7 +110,7 @@ There are two very cool aspects of Haskell/GHCI that are being used here:
    ```haskell
    λ> dir <- pwd
    λ> dir
    FilePath "/Users/friedrichk/.hsh"
    FilePath "/Users/main/.hsh"
    ```
    2. overloaded strings:
  7. fkurz revised this gist Feb 13, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion haskell-shell.md
    Original file line number Diff line number Diff line change
    @@ -67,7 +67,7 @@ Can Haskell be a good replacement for Bash—or similar languages—as a shell s
    4. Run Haskell Shell and have fun :raised_hands:
    ```bash
    $ hsh -e git -e latexmk -e typora
    $ hsh -e git
    [INFO] Creating hsh config
    [INFO] hsh setup done
    [INFO] Creating function for command git
  8. fkurz revised this gist Feb 13, 2021. 1 changed file with 43 additions and 42 deletions.
    85 changes: 43 additions & 42 deletions haskell-shell.md
    Original file line number Diff line number Diff line change
    @@ -29,66 +29,67 @@ Can Haskell be a good replacement for Bash—or similar languages—as a shell s

    ```bash
    hsh_setup() {
    local hsh_home="${HOME}/.hsh"
    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"
    }
    mkdir -p ${hsh_home}

    hsh_extend() {
    local hsh_home="${HOME}/.hsh" \
    command=${1}
    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"
    }

    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_extend() {
    local hsh_home="${HOME}/.hsh" \
    command=${1}

    hsh_init() (
    local hsh_home="${HOME}/.hsh"
    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_setup
    hsh_init() (
    local hsh_home="${HOME}/.hsh"

    while getopts "e:" option; do
    case ${option} in
    e) hsh_extend ${OPTARG}
    esac
    done
    hsh_setup

    cd ${hsh_home}
    stack ghci
    )
    while getopts "e:" option; do
    case ${option} in
    e) hsh_extend ${OPTARG}
    esac
    done

    alias hsh=hsh_init
    cd ${hsh_home}
    stack ghci
    )

    alias hsh=hsh_init
    ```
    4. Run Haskell Shell and have fun :raised_hands:
    ```bash
    $ hsh -e git -e latexmk
    [INFO] Creating hsh config
    [INFO] hsh setup done
    [INFO] Creating function for command git
    [INFO] Creating function for command latexmk
    Note: No local targets specified, so a plain ghci will be started with no package hiding or package options.
    $ 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
    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 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 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.
    * 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
    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. :rocket:
  9. fkurz revised this gist Feb 13, 2021. 1 changed file with 57 additions and 70 deletions.
    127 changes: 57 additions & 70 deletions haskell-shell.md
    Original file line number Diff line number Diff line change
    @@ -25,83 +25,70 @@ Can Haskell be a good replacement for Bash—or similar languages—as a shell s
    stack install turtle
    ```

    3. Define functions to setup/init hash and create an alias _hsh_
    3. Define functions to setup/init/extend Haskell shell and create an alias _hsh_

    ```bash
    # Haskell shell (hsh)
    setup_hsh() {
    set -f
    local binary_name \
    sanitized_binary_name \
    hsh_config="${HOME}/.hsh/.ghci" \
    reserved_keywords=("case" "class" "data" "default" "deriving" "do" "else" "forall"
    "if" "import" "in" "infix" "infixl" "infixr" "instance" "let" "module"
    "newtype" "of" "qualified" "then" "type" "where" "_"
    "foreign" "ccall" "as" "safe" "unsafe")
    # TODO incomplete
    turtle_functions=("cd" "pwd" "ls")

    mkdir -p ~/.hsh

    printf "[INFO] Creating hsh config\n"
    printf ':set -XOverloadedStrings\n:set prompt "λ> "\n:set +m\nimport Turtle\nimport Prelude hiding (FilePath)' > "${hsh_config}"

    for p in $(print -rC1 -- $commands); do
    binary_name=${p##*/};
    sanitized_binary_name=$(echo "${binary_name//[^[:alnum:]]/_}" | tr '[:upper:]' '[:lower:]')

    if [[ "${sanitized_binary_name}" =~ ^[a-z_][a-zA-Z_]*$^ ]] \
    && ! [[ ${reserved_keywords[*]} =~ "${sanitized_binary_name}" ]] \
    && ! [[ ${turtle_functions[*]} =~ "${sanitized_binary_name}" ]]; then
    printf "\r[INFO] Creating function for command %-25s" "${binary_name}"
    printf "\n%2\$s = \\\cl -> shell (\"%1\$s \" <> cl) empty\n%2\$s' = \\\cl -> shell (\"%1\$s \" <> cl)" "${binary_name}" "${sanitized_binary_name}" >> "${hsh_config}"
    fi
    done

    printf "\n[INFO] hsh setup done"
    }

    hsh_init() {
    set -f
    local hsh_config="${HOME}/.hsh/.ghci" \
    run_setup="false"
    while getopts "s" option; do
    case "${option}" in
    s) run_setup="true";;
    esac
    done

    { [[ ! -f ${hsh_config} ]] || [[ ${run_setup} == "true" ]] } && setup_hsh

    (cd "${HOME}/.hsh" && stack ghci)
    }

    alias hsh=hsh_init
    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 :raised_hands:
    ```bash
    $ hsh

    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.0

    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/friedrichk/.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/friedrichk/.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/friedrichk/.ghc/ghci.conf
    Loaded GHCi configuration from /Users/friedrichk/.hsh/.ghci
    Loaded GHCi configuration from /private/var/folders/fc/p5cjtnhd6sdfsbp9tfbtlmqnskrnk7/T/haskell-stack-ghci/2a3bbd58/ghci-script
    $ hsh -e git -e latexmk
    [INFO] Creating hsh config
    [INFO] hsh setup done
    [INFO] Creating function for command git
    [INFO] Creating function for command latexmk
    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. :rocket:
  10. fkurz revised this gist Feb 13, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion haskell-shell.md
    Original file line number Diff line number Diff line change
    @@ -127,7 +127,7 @@ There are two very cool aspects of Haskell/GHCI that are being used here:
    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
    * 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
    ```haskell
    λ> echo "Hi!"
  11. fkurz created this gist Feb 13, 2021.
    165 changes: 165 additions & 0 deletions haskell-shell.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,165 @@
    # 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

    ```bash
    brew install haskell-stack
    ```

    2. Install [turtle](http://hackage.haskell.org/package/turtle) using Stack

    ```bash
    stack install turtle
    ```

    3. Define functions to setup/init hash and create an alias _hsh_

    ```bash
    # Haskell shell (hsh)
    setup_hsh() {
    set -f
    local binary_name \
    sanitized_binary_name \
    hsh_config="${HOME}/.hsh/.ghci" \
    reserved_keywords=("case" "class" "data" "default" "deriving" "do" "else" "forall"
    "if" "import" "in" "infix" "infixl" "infixr" "instance" "let" "module"
    "newtype" "of" "qualified" "then" "type" "where" "_"
    "foreign" "ccall" "as" "safe" "unsafe")
    # TODO incomplete
    turtle_functions=("cd" "pwd" "ls")

    mkdir -p ~/.hsh

    printf "[INFO] Creating hsh config\n"
    printf ':set -XOverloadedStrings\n:set prompt "λ> "\n:set +m\nimport Turtle\nimport Prelude hiding (FilePath)' > "${hsh_config}"

    for p in $(print -rC1 -- $commands); do
    binary_name=${p##*/};
    sanitized_binary_name=$(echo "${binary_name//[^[:alnum:]]/_}" | tr '[:upper:]' '[:lower:]')

    if [[ "${sanitized_binary_name}" =~ ^[a-z_][a-zA-Z_]*$^ ]] \
    && ! [[ ${reserved_keywords[*]} =~ "${sanitized_binary_name}" ]] \
    && ! [[ ${turtle_functions[*]} =~ "${sanitized_binary_name}" ]]; then
    printf "\r[INFO] Creating function for command %-25s" "${binary_name}"
    printf "\n%2\$s = \\\cl -> shell (\"%1\$s \" <> cl) empty\n%2\$s' = \\\cl -> shell (\"%1\$s \" <> cl)" "${binary_name}" "${sanitized_binary_name}" >> "${hsh_config}"
    fi
    done

    printf "\n[INFO] hsh setup done"
    }

    hsh_init() {
    set -f
    local hsh_config="${HOME}/.hsh/.ghci" \
    run_setup="false"
    while getopts "s" option; do
    case "${option}" in
    s) run_setup="true";;
    esac
    done

    { [[ ! -f ${hsh_config} ]] || [[ ${run_setup} == "true" ]] } && setup_hsh

    (cd "${HOME}/.hsh" && stack ghci)
    }

    alias hsh=hsh_init
    ```
    4. Run Haskell Shell and have fun :raised_hands:
    ```bash
    $ hsh

    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.0

    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/friedrichk/.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/friedrichk/.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/friedrichk/.ghc/ghci.conf
    Loaded GHCi configuration from /Users/friedrichk/.hsh/.ghci
    Loaded GHCi configuration from /private/var/folders/fc/p5cjtnhd6sdfsbp9tfbtlmqnskrnk7/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. :rocket:
    ### The Good
    There are two very cool aspects of Haskell/GHCI that are being used here:
    1. monads: most Turtle functions like `dir` and `echo` are monadic
    ```haskell
    λ> :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
    ```haskell
    λ> dir <- pwd
    λ> dir
    FilePath "/Users/friedrichk/.hsh"
    ```
    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
    ```haskell
    λ> echo "Hi!"
    ```

    3. printing of return values: return of echo is something that is printable and GHCI therefore prints it nicely

    ```haskell
    λ> echo "Hi!"
    Hi!
    ```

    The [Turtle tutorial on Hackage](https://hackage.haskell.org/package/turtle-1.5.21/docs/Turtle-Tutorial.html) 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](https://iterm2.com/), we can start _hsh_ in every new window/tab in order to get a complete Haskell based Shell feeling.

    ## Future Work

    [*ptghci*](https://github.com/litxio/ptghci) adds useful features like syntax highlighting and tab completion and might be a good extension.


    ---

    ## SOURCES

    * https://downloads.haskell.org/~ghc/7.6.3/docs/html/users_guide/ghci-dot-files.html