Monday, August 31, 2009

EDSL done...

The expect embedded Domain Specific Language portion of my code has turned out to be quite the useful little sublanguage to flexibly write little scripts in for controlling, over various channels (ssh, telnet, local comm ports perhaps), devices.

I probably can't document too much about it here, as it's company IP, but I can tell you that building up an EDSL in Haskell is not near as difficult as I thought, at least in the case I've implemented. It's a matter of choosing the right Monad Transformer base, or writing your own (which could take a long time actually depending on how well you can wrap your head around the sequencing of the various actions you'd like to take). Also once you realize that the aggregation and sequencing of simpler actions can be used to build up more complex ones, you're pretty much golden and have learned to realize the full power of the Monad concept.

It's also one thing to see it, or even read about it, but the real eye-opening bit is when you actually implement one that's useful to you on your own. Then you almost want to beat your chest and proclaim you're the new king of the tribe.

I love a programming language that you can use to build specialized tools ,*very* quickly, that meet your immediate need.

My boss even almost threw me a curve ball on the way he thinks I should utilize this technique. Then I realized that what I'd written could most easily support the sort of idea he was after because I picked the right abstractions.

I'm very grateful that this technology even exists, as it is saving us a lot of time where I work, making progress on this sort of programming that otherwise seems a ton of work.

Haskell is definitely for the win today :-)

Tuesday, August 25, 2009

Haskell and DSLs

One of the great features of Haskell is the ability to write programs that execute in contexts that are separate from the rest of your program. Monads are one of those kinds of contexts, and a monad allows you to define exactly what happens when you sequence different expressions within a context. Using this, one can write little imperative DSLs (Domain Specific Languages) that execute along side the rest of your Haskell code.

There's DSLs for controlling "robots" in Hudak's Haskell School of Expression book. There's a DSL that is a "vintage basic". There's another one for controlling small realtime systems called Atom.

The possibilities seem endless.

I've been considering making one of these as a replacement for the old Unix program expect, or at least a subset of it that I'm interested in. Haskell's already got very strong regular expression libraries, and some of the best parser writing libraries I've seen. Having something like the expect syntax for interacting with remote services or devices would be excellent in conjunction with those other capabilities.

I found a tutorial online today about DSLs that seems pretty good. We'll see if I ever get to the point of implementing one.

Thursday, August 20, 2009

Interesting Discussion

I've been talking Monads with folks on haskell-cafe as well as lazy-IO, and understanding strictness and where it sometimes needs to be sprinkled in to get the behavior desired from a lazy IO system.

This is sort of the price paid for wanting to completely separate one's pure data processing code from IO. The advantage of having this separation is of course in testing of code, in that pure code has referential transparency, meaning that you can call it any number of times you like, and as long as you provide the same X for input, you will always receive the same Y as a result.

By putting Input and Output operations into their own context of evaluation, we totally avoid certain classes of problems in software writing, so the effort seems worth it.

Needless to say it's been an interesting discussion, linked here.

Tuesday, August 18, 2009

Posixy stuff and Haskell

The GHC Haskell compiler and library set it comes with has a nice bit of POSIX functionality. Today, however, I found myself really wanting for a program that could popen a program, giving me back a pair of handles to communicate with the external program, and allow me to do Expect-like things with the interaction.

Essentially I need a bot. The bot needs to talk to a device that speaks SMASH CLP, and my initial thoughts were to write some Haskell code around the useful function interact, or rather, a close relative of interact.

Interact has the signature

interact :: (String -> String) -> IO ()

This just means that it takes a function which takes a String as input and produces a String as output, and the result of interact is an IO action that returns ().

What this gives me is the ability to process input strings, to produce output strings, which get read in and written out by the IO action that is the result of interact. It interleaves the pure functional processing around the input to create output.

The way it works is by calling the function getContents, which lazily reads all the input from stdin and processes it through the provided (String -> String) function, to produce output.

At first glance, this is not seemingly a very advantageous function, except when you take into account all the ways you can set up the IO before calling interact to get some nice behaviors.

For example, if one sets the handles for stdin and stdout to "LineBuffer" based buffers before calling interact, you can get linewise input by doing the following inside the (String -> String) processing function.

lineId = unlines . lines

The function lineId breaks up the lazy input string into lines, then re-assembles them by composing the function lines and unlines together.

The type of the function lines is:

lines :: String -> [String]

The type of the function unlines is

unlines :: [String] -> String

One would think that these two functions would just negate each other, and they do when combined as "unlines . lines" returning the original input to lines, however, if I add further behaviors to the function composition expression that operate on the [String] result of lines before running unlines I have the opportunity to work on each member of the list.

This gives me guaranteed linewise input evaluation of commands, and the ability to interpret line-oriented protocols, strings or what have you.

Now think of how useful interact can be?

Now, interact by itself is only dealing with stdin and stdout. Perhaps I want to open a connection to a remote service, or even a stream of in and out data. It is not terribly hard to write a relative to interact that can handle those sources of data.

hInteract :: Handle -> Handle -> (String -> String) -> IO ()
hInteract inp out f = hPutStr out . f =<< hGetContents inp

This function can take a handle for input, a handle for output, and a function of String -> String, and uses them to lazilly read input from the input handle, pushing data through the String -> String function, to finally output the result string to the output handle.

All of the interesting parts of this function are still contained in f.

The part that's kind of exciting now is that I don't have to limit myself to just line based input if I consider using functions like words and unwords.

I think I'm nearly ready to write an almost IO free, pure version of my Expect-like code bot in Haskell.

Monday, August 10, 2009

Keepin Busy

Been trying to get a handle on "nice looking Haskell" network code. I've got some tasks at work that might actually require me to do some network coding in Haskell so that feeds nicely into my 9p implementation in Haskell, but, at the same time, I'm very concerned about data sizes and understanding how Haskell programs garbage collect, and understanding the waxing and waning of data allocation sizes in a lazy language.

I've not revisited 9p in general in a while. Still hoping to get a native Plan 9 box up and running somewhere that I can play around with.

I do want to get back to this project though. Every once in a while, I look at things like Facebook's Thrift, or other RPC mechanisms that are meant to be language neutral, and I think "this could have been 9p". It's interesting to try anyway just to understand the limitations of one thing vs another.