April 9, 2012 - Tagged as: haskell, en.
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
= (0, 0)
state0
addAvg :: Int -> AvgState
= do
addAvg x <- get
(count, total) +1, total+x)
put (countreturn $ (total+x) `div` (count+1)
test :: AvgState
= do
test 10
addAvg 20
addAvg 30
addAvg
main :: IO ()
= do
main 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 Control.Monad.State
package.
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: Parsec
.
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
= many1 (letter <|> oneOf "-" <|> digit)
chanName
msgCmd :: Parser Cmd
= do
msgCmd "msg"
string
spaces<- chanName
chan
spaces<- many1 anyChar
msg
eofreturn $ 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.