![]() |
|
![]() |
|
|
Thread Tools | Display Modes |
|
|
#1 |
|
Hobbyist Programmer
|
Haskell fun: hsh -- a busybox spinoff in haskell
So I was trying to figure out exactly what to do with Haskell, as I wanted to experiance the language. I read Don Stewarts excellent "Programming Haskell" 3 part series and it lead me to try and write clones of unix commands in Haskell. So far my program implements roughly 11 commands.
Examples: [austin@continuum ~]$ hsh -v haskell hsh v0.4, compiled with ghc-6.6, on linux-i386 [austin@continuum ~]$ hsh cat < dummy.txt hi i'm austin [austin@continuum ~]$ hsh tac < dummy.txt i'm austin hi [austin@continuum ~]$ hsh wc < dummy.txt 2 [austin@continuum ~]$ hsh uname Linux continuum 2.6.20-ARCH #1 SMP PREEMPT Sat Mar 10 10:12:25 CET 2007 i686 [austin@continuum ~]$ hsh whereis gcc gcc: /usr/bin/gcc [austin@continuum ~]$ hsh ls /tmp ... [austin@continuum ~]$ echo -e "dont mess with me\ndue" | hsh grep "do" dont mess with me [austin@continuum ~]$ It also implements other commands such as rmdir, cp, rm, mv, mvdir (had to seperate the two), etc. (grep is my favorite.) For anybody just wanting the source code: http://paste.lisp.org/display/37726#4 If anybody would like to try it out, especially on other compilers (which I would appreciate,) you can get a source tarball here: http://austin.youareinferior.net/scr...0070315.tar.gz (linux/bsd only) If you try it, tell me what haskell compiler and version you use; I have only tested this on GHC 6.6 (so any hugs98 or nhc98 users are appreciated.) Compilation instructions are included in the README file, all you need is a Compiler and Cabal (I decided to try it for this and absolutely loved it as a build system. I wish more were like it.) If anybody wants to try it but doesn't have a Haskell compiler, I'm not giving out binaries right now. I need to clean up the option code *a lot* (I figured out Haskell's Getopt, so I'll use that for the option code in the v0.5 release.) Otherwise, you wouldn't probably really know how to work it without looking at the source (at least that's how I feel.) If anybody wants to upload a .tar.gz with it in the topic, that'd be fine as well.
__________________
happy openBSD and linux user; huhu #perl 6
("hello "~++$i~"!").say while $i < 10; |
|
|
|
|
|
#2 |
|
Programming Guru
![]() Join Date: Aug 2005
Location: England
Posts: 1,499
Rep Power: 4
![]() |
I had some fun pouring through your code, and I hope you won't be offended if I say that I noticed a few areas that look like they could be improved. All those "suceeds" add up to a lot of redundancy, and I wanted to see if Haskell had some mechanism of cutting them down.
I remembered reading an article on Haskell error handling, and I decided that throwDyn and catchDyn looked the way to go. I wrote out some test code, and after solving the most obvious errors, I eventually got: test.hs:6:5:
Can't make a derived instance of `Typeable ShellError'
(You need -fglasgow-exts to derive an instance for this class)
When deriving instances for type `ShellError'My first step, then, was to define an error type to handle all potential errors: data ShellError = UsageError | AppError String String
deriving (Typeable)Next, I put the ShellError type in the Show type class. This will allow me to use the "show" function on my errors: usage = "usage: hsh [COMMANDS...] [OPTIONS]"
instance Show ShellError where
show UsageError = usage
show (AppError app msg) = printf "%s: %s" app msgNext, I'll use catchDyn to create a simple wrapper function: handleErrors m = catchDyn m handler
where handler :: ShellError -> IO ()
handler err = hPutStrLn stderr (show err) >>
exitWith (ExitFailure 1)Armed with handleErrors, I wrapped my main function in it: main = handleErrors (getArgs >>= parse) parse [] = throwDyn UsageError
parse ("ls":xs) = ls xsensure :: IO Bool -> ShellError -> IO () -> IO ()
ensure pred err m = do x <- pred
if x then m else throwDyn errAnyway, all that's left is my ls function: ls [] = ls ["."]
ls (x:xs) =
ensure (doesDirectoryExist x) dirError $
getDirectoryContents x >>= mapM_ putStrLn
where dirError = AppError "ls" (printf "directory '%s' does not exist" x)Anyway, that's the best I could come up with, but I'm still a Haskell newbie too, so I'm sure this can be improved further. I'm not really that happy with the "ensure" function. It doesn't seem that neat to me. Here's the full source for reference: import IO
import System.Exit
import System.Directory
import System.Environment
import Data.Typeable
import Control.Exception
import Text.Printf
usage = "usage: hsh [COMMANDS...] [OPTIONS]"
data ShellError = UsageError | AppError String String
deriving (Typeable)
instance Show ShellError where
show UsageError = usage
show (AppError app msg) = printf "%s: %s" app msg
handleErrors m = catchDyn m handler
where handler :: ShellError -> IO ()
handler err = hPutStrLn stderr (show err) >>
exitWith (ExitFailure 1)
main = handleErrors (getArgs >>= parse)
parse [] = throwDyn UsageError
parse ("ls":xs) = ls xs
ensure :: IO Bool -> ShellError -> IO () -> IO ()
ensure pred err m = do x <- pred
if x then m else throwDyn err
ls [] = ls ["."]
ls (x:xs) =
ensure (doesDirectoryExist x) dirError $
getDirectoryContents x >>= mapM_ putStrLn
where dirError = AppError "ls" (printf "directory '%s' does not exist" x) |
|
|
|
|
|
#3 |
|
Hobbyist Programmer
|
Thanks for the comments. :>
Last night I was talking to a friend and decided to totally rewrite it. Renaming it to version 1.0 was pretty much obligatory. The new version is substantially different. Instead of having to hard code commands in, the new version is extensible. All you have to do is provide a name that the command will respond to and a callback function. Write your function in a module, and when someone calls say, "hsh ls" it'll execute the LS function. You only have to import your module into a special core module, and add an entry to a list for your command to work (and if you want to distribute your program as source using Cabal's "setup.hs sdist," you must slightly modify the hsh.cabal file, but that's all.) When your command is integrated, configure and build (Cabal script provided, just run 'runhaskell Setup.hs ...') The usefulness of this is that the main core really doesn't "exist" as far as commands go. All commands take a type [String] and return IO () (I added type signatures to most of the base code, but this shouldn't really be a limitation.) The [String] is the command line arguments passed to your program. This means the commands can be as simple or complex as you want. For example, you could write a new grep implementation that instead of just supporting searching like in my example, it uses the System.Console.GetOpt library and accepts much more complex arguments. This also is a benefit as the core (there're only 4 files in the core, the other ones in the source tree are command modules to be included) is cross platform, so you could write unix/win32 specific commands and modules. The 1.0 distribution, however, comes with some modules that use non-portable libraries, so don't expect this to work on Windows right out of the box (this development was done on my linux system.) I figured this was the best way to accomplish having an extensible program, just let the users build their commands in if they need them. If Haskell was more easily embeddable in Haskell, I probably would have allows the use of literate haskell source code. I would have followed one of the many tutorials on Scheme in Haskell, but this would really limit what you'd be able to write. So just building modules into the executable seemed like the best code. The code is still pretty naive and basic, so for maybe a 1.1 release I'll beef up the error handling a lot more. I really just need a README before the 1.0 release is done so you could write a module, but here's the source code, I'll assume you can figure it out (building it is just like building any other cabal application): http://austin.youareinferior.net/scr...0070316.tar.gz
__________________
happy openBSD and linux user; huhu #perl 6
("hello "~++$i~"!").say while $i < 10; |
|
|
|
![]() |
| Bookmarks |
| Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
| Thread Tools | |
| Display Modes | |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| Haskell | Arevos | Other Programming Languages | 25 | Jun 3rd, 2007 4:34 PM |
| Haskell ideas? | Mad_guy | Other Programming Languages | 3 | Feb 5th, 2007 9:16 PM |