{- | 'IO'-specific functionality around the random selection algorithm described in "Data.Random.Choose". -} {-# LANGUAGE ScopedTypeVariables #-} module Data.Random.Choose.IO ( getSelectionsIO ) where -------------------------------------------------------------------------------- import qualified Data.Random.Choose.Tree as Tree import Data.Random.Choose.Tree (Tree) import Control.Monad.Random (evalRandIO) import qualified Data.Foldable as Foldable import Data.Function ((&)) import Data.List (sort) -------------------------------------------------------------------------------- getSelectionsIO :: forall a. (Ord a) => IO (Maybe a) -- ^ Producer of items to choose from; produces 'Nothing' -- when there are no more items) -> Int -- ^ /n/: Number of items to choose -> IO [a] -- ^ /n/ of the items (or all of the items if fewer than -- /n/ are produced) -- ^ Selects /n/ items uniformly at random from an input stream. -------------------------------------------------------------------------------- getSelectionsIO getItem limit = f 0 Tree.empty where -- We store each line of text along with its index (i) so that when we're -- done, we can sort by the index, thus outputting the selected items in -- the same order in which they were read. f :: Int -> Tree (Int, a) -> IO [a] f i tree = do lineMaybe <- getItem case lineMaybe of -- We read a line; insert it into the tree and recurse. Just line -> do tree' <- evalRandIO ( tree & Tree.insert (i, line) & Tree.applyLimit limit ) f (i + 1) tree' -- We've reached the end of the input. Convert the tree to a list, -- sort it, and strip out the indices to return just the text. Nothing -> return $ snd <$> sort (Foldable.toList tree)