I wrote an FsCheck generator that yields random glob syntax patterns (e.g. a*c?
) along with a random string that matches the pattern (e.g. abcd
). However my solution uses a mutable variable and I'm rather ashamed of that. Have a look:
open FsCheck
type TestData = {Pattern: string; Text: string}
let stringFrom alphabet =
alphabet |> Gen.elements |> Gen.listOf |> Gen.map (List.map string >> List.fold (+) "")
let singleCharStringFrom alphabet =
alphabet |> Gen.elements |> Gen.map string
let matchingTextAndPatternCombo = gen {
let toGen = function
| '*' -> stringFrom ['a'..'f']
| '?' -> singleCharStringFrom ['a'..'f']
| c -> c |> string |> Gen.constant
let! pattern = stringFrom (['a'..'c']@['?'; '*'])
let mutable text = ""
for gen in Seq.map toGen pattern do
let! textPart = gen
text <- text + textPart
return {Pattern = pattern; Text = text}
}
Notice that text
is mutable and how its value is accumulated in a loop.
My guts tell me that there must be a way to fold
the generators into text
, but I can't figure how, because I don't understand how let!
works under the hood (yet). I was considering something similar to the following:
let! text = pattern |> Seq.map toGen |> Seq.fold (?) (Gen.constant "")
Am I on the right track? What should the accumulator and seed for fold
look like?