My first Haskell: dice rolling Yesterday morning I decided to stop procrastinating and start learning me some Haskell.

So far I've made this, which is a simple cli 'dice rolling' utility that can be used like:

$ ./dice 3d20
You rolled: 17


The code looks like this:

import Data.List.Split
import Data.Char
import Control.Monad
import Control.Monad.Random
import System.Environment


type Dice = (Int, Int)

diceCode :: String -&gt; Dice
diceCode die = (parts!!0, parts!!1)
  where
    parts = [read x :: Int | x &lt;- take 2 (splitOn "D" (map toUpper die))]


rollDie :: (RandomGen g) =&gt; Int -&gt; Rand g Int
rollDie sides = getRandomR (1, sides)

rollDice :: (RandomGen g) =&gt; Dice -&gt; Rand g Int
rollDice dice = liftM sum (sequence(replicate rolls (rollDie sides)))
  where
    rolls = fst dice
    sides = snd dice


main = do
  args &lt;- getArgs
  roll &lt;- evalRandIO (rollDice (diceCode (args!!0)))
  putStrLn ("You rolled: " ++ show roll)


I was able to get the diceCode 'parsing' function by myself without too much trouble.

The rollDie function is almost straight from an example in the Control.Monad.Random docs, which helped a lot.

I struggled for quite a while to find a recipe for summing the rolls in rollDice... it seemed to me I ought to use msum but I couldn't find a way to make it work. liftM sum seems to do exactly what I wanted though.

I also found the use of tuples quite cumbersome. In Python I could just do:
    rolls, sides = dice
but I seem to have to use the horribly-named fst and snd functions to access the members in Haskell (?)

I guess the next part of my adventure is to try and incorporate this into a larger program, eg a simple game.  It seems like the monadically-wrapped random int values are going to force the rest of the code to be 'monad-aware' (i.e. lots of use of liftM) and I wonder if there is a way to avoid this?
The list comprehension you use in diceCode is a bit overkill: after all, you only want to split the list of characters into two. You can use break instead.

Here I have to use fmap tail to act on the second list because break retains the value it breaks the list at (the 'd' character here). This would be a good place to handle the sort of errors that would arise if there is no 'd' in the string.

diceCode :: String -&gt; Dice
diceCode die = (read rolls, read sides) where
  (rolls, sides) = fmap tail $ break ('D' ==) $ fmap toUpper die


Your rollDice is a bit complex. Here is how I would rewrite it:


(rolls, sides) on the left hand side exposes the two components of the tuple
[1..rolls] generates a list of length rolls
mapM (const $ rollDie sides) replaces all the numbers in that list with an invocation of rollDie sides and performs the same job as sequence thus returning an Random g [Int]
sum &lt;$&gt; (or fmap sum $) goes under the Random g part and sums the elements in the [Int] value.


Putting all this together we get:

rollDice :: (RandomGen g) =&gt; Dice -&gt; Rand g Int
rollDice (rolls, sides) = sum &lt;$&gt; mapM (const $ rollDie sides) [1..rolls]


The rest of the code is quite idiomatic. One thing I was a bit concerned about is the fact that a Dice is represented as a pair of Ints: to understand which is which, you need to read the code manipulating them and if you make a mistake the compiler won't warn you: they have the same type! It's quite annoying. One thing you could do is use a record type instead in order to name the two fields:

data Dice' = Dice' { rolls :: Int
                   , sides :: Int }


This way you can access them by their names, build the Dice' using the named syntax, etc.