I had written a short post about Haskell and monads to Nathan’s University forum as a first homework, and I wanted to add it to my blog too:
I know lots of people here have already given Haskell as an example, but I want to mention to a different point of Haskell. Monads and DSL capabilities.
Every monad in Haskell is potentially a DSL. You can define commands(ie. functions) in a syntax that looks almost like syntax in imperative languages even if you’re doing a purely functional computation(for imperative computations, see IO monad). When you write a monad and some functions working with this monad, you basically write operations of a kind of computations, and a way to combine this computations(with
>>= function, read as bind).
This gives you two great advantages. First, monads give you an elegant way to separate combination and calculation logic, and second, it gives you an opportunity to create syntactic abstractions.
For example, you don’t have to pass some states around functions thank to monads. You can just create a monad with functions getting a state and returning some values and the new state. Then you can define your combination logic(bind functions) and with the help of
do notation, you can write almost imperative looking code, passing states automatically. See example:
import Control.Monad.State import Control.Monad type AvgState = State (Int, Int) Int state0 = (0, 0) addAvg :: Int -> AvgState addAvg x = do (count, total) <- get put (count+1, total+x) return $ (total+x) `div` (count+1) test :: AvgState test = do addAvg 10 addAvg 20 addAvg 30 main :: IO () main = do print $ evalState test state0
Here I’m calculating arithmetic average of some integers.
type AvgState is my data type representing the sum of the numbers I give and the total count of numbers. Here I don’t write a new monad, instead I use Haskell’s State monad, contained in
addAvg functions is the main logic. If you look at it, it almost looks like an imperative program, I’m reading some values and changing them by adding them one, and returning a new value(note that I’m not returning any new states, it’s being handled my the monad itself), but still it’s purely functional.
Now how’s that a DSL? Look at
test function and hopefully you’ll see :) .
I want to give another example about DSL-like monads:
I’ve been working on a Websocket based chat protocol written in Haskell lately and this code is directly from my project:
chanName :: Parser ChanName chanName = many1 (letter <|> oneOf "-" <|> digit) msgCmd :: Parser Cmd msgCmd = do string "msg" spaces chan <- chanName spaces msg <- many1 anyChar eof return $ MsgCmd chan msg
I’m using Parsec’s
Parser monads with
do notation and it looks almost like Backus–Naur Form. chanName mathes list of letters, ‘-’ character, or digits with at least one element. This is a parser. And then I’m using this parser in my
msgCmd parser. It matches a string “msg”, then arbitrary number of spaces, then
chanName, then spaces again, and at last any characters.