A Haskell shell.
Here lies a prototype/experiment for the following question: can the normal Haskell REPL make a passable shell if it has file completion and directory awareness?
It's a simple read-eval-print loop for Haskell that has some simple awareness of the current directory and completion works. I whipped this up in a few hours, it's only a couple hundred lines of code.
What's it like?
It looks like this:
Welcome to Hell! chris:~/$ ls Backups Desktop Downloads Emacs Mail Pictures Renoise Samples Books Documents Dropbox Flash Org Projects Repos Scripts
It has some UNIX basics:
chris:~/$ cd "Projects/hell/" chris:~/Projects/hell$ ls dist examples hell.cabal LICENSE README.md src TAGS chris:~/Projects/hell$ ls "-a" . .. dist examples .ghci .git .gitignore hell.cabal LICENSE README.md
All functions are based on shell-conduit and are variadic. They are generated by taking all binaries in your PATH.
The only exception (so far) is
cd. There is no binary called
POSIX because it is a shell concept.
How does it work?
It uses the GHC API (so please submit a pull request if it doesn't work with your GHC version) and the Haskeline package to make a simple read-eval-print loop, keeping track of any current directory changes. The Haskeline package does completion at the prompt built-in.
It tries to run the line as an
IO a statement:
chris:~/$ cd "."
If that's the wrong
type, it runs it as a shell-conduit
chris:~/$ ls $| grep "E" LICENSE README.md
Or finally it evaluates
it as an expression, printing the result with
chris:~/$ 2 * 3 forall a. GHC.Num.Num a => a 6
I think it's pretty neat to have Haskell evaluation in your shell. I often open GHCi to do little arithmetical calculations.
If it can't find any way to run it or print the value, it just displays the type:
chris:~/$ head forall a. [a] -> a
The commands of GHCi like
:k are not supported at this
time. Top-level bindings are not yet supported either.
It supports completion of function names and binaries:
chris:~/$ get getChar getfacl getZipSink getContents gethostip getZipSource getContents getkeycodes getAppUserDataDirectory getLine getopt getCurrentDirectory getLine getpcaps getDirectoryContents getFpco gettext getHomeDirectory getafm gettextize getModificationTime getcap gettextsh getPermissions getconf getty getTemporaryDirectory geteltorito getweb getUserDocumentsDirectory getent getZipConduit
chris:~/Pictures$ ls "Screenshot from 2014-0 Screenshot from 2014-08-13 22:05:10.png Screenshot from 2014-09-09 14:55:46.png Screenshot from 2014-09-15 17:09:26.png Screenshot from 2014-08-11 11:39:00.png Screenshot from 2014-09-13 19:03:34.png Screenshot from 2014-09-12 12:26:51.png Screenshot from 2014-08-13 12:57:14.png Screenshot from 2014-09-15 17:03:34.png Screenshot from 2014-08-15 18:19:03.png Screenshot from 2014-08-28 16:50:04.png
Why “Hell”? Surely a Haskell shell would be heaven!
It's an ironic name, like Little John. And who knows, a Haskell shell might be hell.
You should add loads of custom syntax!
That's not going to happen. Hell is about plain Haskell.