user1897830
user1897830

Reputation: 443

Changing Types in Haskell

I wish to construct a random string consisting of only alphabetical characters. This is the code I have so far:

letters :: String
letters = "abcdefghijklmnopqrstuvwxyz"

{- | Generates any lower case alpha character.
-}
lowerAlpha :: Gen Char
lowerAlpha = oneof (map return letters)

getString :: Int -> String
getString 0 = []
getString n = lowerAlpha : getString (n - 1)

getString is passed a random Int between 1 and 25. Because lowerAlpha returns a Gen Char getString wont work as it expects a Char in order to build a String. Is there a way to change a Gen String to a String? This is the line of code where the problem occurs - getString n = lowerAlpha : getString (n - 1)

Upvotes: 0

Views: 356

Answers (3)

leftaroundabout
leftaroundabout

Reputation: 120711

Is there a way to change a Gen String to a String?

No, emphatically not. For some reason, people coming from other programming languages always seem to think “they're both string-kinds of types, so surely they're convertible”. Nope! Gen String is something completely, fundamentally different from String. The latter is just a plain data value – a list of characters. But Gen String is a generator, an action that generates strings. Assuming this can be converted to a string is like assuming a cow can be converted to a milk bottle.

The correct thing is for course for getString to also have a Gen type, because generating strings is what it does.

getString :: Int -> Gen String

The question is how to implement that. Well, it's guessworking because you didn't specify what Gen is exactly, but such type constructors in Haskell are usually applicative functors. That allows you to write

getString 0 = pure []
getString n = (:) <$> lowerAlpha <*> getString (n - 1)

As gallais remarks, this can also be written shorter as

getString n = replicateM n lowerAlpha

Upvotes: 7

user1897830
user1897830

Reputation: 443

Thanks for your assistance. This is what I came up with. Works well. str <- listOf (choose ('a','z'))

Upvotes: 0

ErikR
ErikR

Reputation: 52029

Perhaps you are looking for vectorOf, i.e.:

import Test.QuickCheck

getString :: Int -> Gen String
getString n = vectorOf n lowerAlpha

To actually generate a random string you can use generate (requires the IO-monad):

main = do
  generate (getString 25) >>= putStrLn

or just from the GHCI repl:

ghci> generate (getString 25)
"siebrxrlvuuxdvhqwcfykqwdc"
ghci> generate (getString 3)
"rcv"

Upvotes: 3

Related Questions