TheIronKnuckle TheIronKnuckle - 9 months ago 71
C# Question

Trouble translating small C# "Random String" function to Haskell

I saw the following code in my companies codebase and thought to myself "Damn that's a fine line of linq, I'd like to translate that to Haskell to see what it's like in an actual functional language"

static Random random = new Random();
static string RandomString(int length)
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)])

However I'm having a bit of trouble getting a concise and direct translation to Haskell because of how awkward it is to generate random numbers in this language.

I've considered a couple of approaches. The most direct translation of the C# code only generates a single random index and then uses that in place of the
. But I need to generate multiple indexes, not a single one.

Then I considered doing a list of
IO Int
random number actions, but I can't work out how to go through and convert the list of
actions into actual random numbers.

So the Haskell code that I end up writing ends up looking quite convoluted compared to the C# (which I wasn't expecting in this case) and I haven't even got it to work anyway.

My question, what would be a natural translation of the C# to Haskell? Or more generally, how would you go about generating a random String of a specified length in Haskell (because this C# way doesn't seem to translate well to Haskell)?

Note: I'm mainly interested in what the algorithm to generate a random string looks like in Haskell. I'm not really interested in any standard libraries which do the job for me

Answer Source

The natural translation to Haskell involves having some sort of IO (as you need randomness). Since you are essentially trying to perform the action of choosing a character n times, you want to use replicateM. Then, for getting a random number in a range, you can use randomRIO.

import Control.Monad (replicateM)
import System.Random (randomRIO)

randomString :: Int -> IO String
randomString n = replicateM n (do r <- randomRIO (0,m); pure (chars !! r))
    chars = ['A'..'Z'] ++ ['0'..'9']
    m = length chars

This is somewhat complicated by the fact you want a string of only characters in a certain range. otherwise, you'd have a one liner: randomString n = replicateM n randomIO.