03-Generating-Random-Data.purs

-- | The focus of this file is on how to use combinators.
-- | to produce random data.
module Test.RandomDataGeneration.Combinators where

import Prelude
import Data.Maybe (fromJust)
import Effect (Effect)
import Effect.Console (log)
import Data.Array.NonEmpty as NEA

-- new imports
-- these are all explained below
import Test.QuickCheck.Gen (
    uniform
  , choose, chooseInt, elements, shuffle
  , oneOf, frequency, suchThat
  , arrayOf, arrayOf1, listOf, vectorOf

-- all imports below this line are needed to compile
  , Gen, randomSample
  )
import Data.Int (even)
import Data.Traversable (for)
import Data.Tuple (Tuple(..))
import Partial.Unsafe (unsafePartial)

-- Prints the results of the combinators in Test.QuickCheck.Gen
main :: Effect Unit
main = do                                                                 {-
  printData "explanation" $
    combinator arg1 arg2 -- if args are required                          -}

  log "*** Basic combinators ***"

  -- see "Standard Uniform Distribution" section in
  -- https://www.wikiwand.com/en/Uniform_distribution_(continuous)
  printData "uniform - standard uniform distribution" $
    uniform

  printData "chooseInt - choose a random Int between" $
    chooseInt 1   10

  printData "choose - choose a random Number between" $
    choose    1.0 10.0

   -- oneToThree = NonEmptyArray [1, 2, 3]
  let oneToThree = unsafePartial fromJust $ NEA.fromArray [1, 2, 3]
  printData ("elements - Choose an random element from the array where \
             \each element has the same probability of being chosen") $
    elements oneToThree

  let array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  printData ("shuffle - randomize the order of an array's elements \
             \(e.g. " <> show array <> ")") $
    shuffle array



  log "*** Composable combinators ***"

  printData ("oneOf - Randomly choose a generator (where each generator has \
             \the same probability of being chosen) and use it to generate \
             \a random instance of the data type") $
    oneOf $ unsafePartial fromJust $ NEA.fromArray
      [ chooseInt   0   9
      , chooseInt  10  99
      , chooseInt 100 999
      ]

  let array_1 = NEA.singleton 1 -- (NonEmptyArray Int)
      array_2 = NEA.singleton 2
      array_4 = NEA.singleton 4
      lessOften = 1.0
      sometimes = 2.0
      moreOften = 4.0
  printData ("frequency - Generate an instance from an array of generators \
                  \where each generator is used unequally") $
    frequency $ unsafePartial fromJust $ NEA.fromArray
      [ Tuple lessOften (elements $ array_1)
      , Tuple sometimes (elements $ array_2)
      , Tuple moreOften (elements $ array_4)
      ]

  printData "suchThat - Create a generator (e.g. even numbers) by filtering \
            \out invalid instances that are generated from another generator \
            \by using the given function" $
    suchThat (chooseInt 1 100) even

  log "*** Multiplier combinators ***"

  printData "arrayOf - Using a generator that creates one `a`, create \
            \an empty array of type `a` or \
            \an array of randomly-generated `a` instances" $
    arrayOf $ chooseInt 0 9

  printData "arrayOf1 - Using a generator that creates one `a`, create \
            \an non-empty array of randomly-generated `a` instances" $
    arrayOf1 $ chooseInt 0 9

  printData "vectorOf - Using a generator that creates one `a`, create \
            \a non-empty array of a specified number of randomly-generated \
            \`a` instances" $
    vectorOf 10 $ chooseInt 0 9

  printData "listOf - Using a generator that creates one `a`, create \
            \a non-empty list of a specified number of randomly-generated \
            \`a` instances" $
    listOf 10 $ chooseInt 0 9

-- Helper functions

printData :: forall a. Show a => String -> Gen a -> Effect Unit
printData explanation generator = do
  log $ "=== " <> explanation
  result <- randomSample generator
  void $ for result (\item -> log $ show item)
  log "=== Finished\n"