Practical Haskell: scripting with types

I had the pleasure to give a new talk today, on design in functional programming — types, abstractions and monads — using the motivating example of scripting. The slides are below and a PDF version is available.

Shell scripts are often a quick, dirty way to get the job done. You glue
together external tools, maybe do a little error checking and process all data
as strings. This is great for some very simple problems but as requirements
change and more is demanded from the code shell scripts become unwieldy and
fragile. When they get large, they become slow and difficult to maintain. If
you need to write robust code then shell is not the way to go.

In this talk at an alternative: how to use Haskell as a type checked and
natively compiled language for scripting tasks. By refining the semantics of
the problem domain, employing abstraction, we produce shorter and more robust
code, that is more maintainable and scalable.

14 thoughts on “Practical Haskell: scripting with types

  1. Finally I got an excuse to start using Haskell at work ;-)

    I don’t seem to have the module “Process” in my system. If I try to run the following script:

    #!/usr/bin/env runhaskell

    import Text.Printf
    import Process

    main = do
    output <- run "ls -l /"
    printf "%s\n" output

    , my Haskell interpreter (Fresh GHC 6.12.3 with latest Haskell Platform) fails with the following error:

    $ runhaskell x.hs

    x.hs:4:7:
    Could not find module `Process':
    Use -v to see a list of the files searched for.
    $ runhaskell –version
    runghc 6.12.3
    $

    Any ideas?

  2. Don,

    Cool article ;-) Thanks.

    I think that on slide 25:

    instance MonadError IO

    should be

    instance MonadError IOError IO

    Also, it’s not clear to me what the ErrorT monad transformer buys you in:

    newtype Shell a = Shell { runShell :: ErrorT String IO a }

    since you’re ignoring the internal Either type from ErrorT. It is just that you get a throwError that you can use with a String instead of with an IOError?

  3. Is that a new case-style construct on slide 13?

    new | old == 100 = 0
    | otherwise = 100

    It gives me a conflicting definition error.

  4. Cool talk! I agree with most of the points you make (and I think a Haskell library optimized for scripting would be a great tool), but one place where we differ is on compilation.

    I think compilation is a great tool for type- and syntax- checking a Haskell script, but to me, to remain a script, the actual executable has to be the source code, not a compiled binary. That way, I can, as a reader of scripts, tell what a script does (and how it does it) just by looking at it, instead of trying to track down the matching executable. There’s a bunch of times where I want to
    * copy some of the behaviour of an existing script
    * trace through a particular script to see if it’s got a bug, or if I’m misunderstanding its use.

    I do these tasks and tasks like them time and time again with shell code. And though Haskell, and good practices like sensible error reporting will help somewhat, it’s no substitute for a human readable shell script.

    So I’ll be sticking with runhaskell.

    Thanks!

  5. “Programmable semicolon” is a great metaphor, and seeing the error handling get abstracted out was an “aha!” moment for me. Plus now I understand the purpose of monadic bind a little better.

    Thanks!

  6. I think that the type of modify on slide 17 should be modify :: Box a -> (a -> b) -> m b. (Actually, it seems to me that modify is just fmap . get, right?)

Leave a comment