packages feed

vinyl-vectors-0.2.0: examples/sorting.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DataKinds #-}
import Data.Vinyl
import Data.Vinyl.TypeLevel (RIndex,RecAll)
import Data.Vinyl.Functor (Identity(..))
import Data.Text (Text)
import GHC.Exts (Constraint)
import Data.Proxy (Proxy(Proxy))
import Lens.Micro ((^.))
import Data.Vector.Vinyl.TypeLevel (ListAll)
import qualified Data.Vector.Vinyl.Default.Empty.Monomorphic as V
import qualified Data.Vector.Algorithms.Intro as Intro
import qualified Data.Vector.Algorithms.Merge as Merge

-------------------------------------------------------------------------------
-- This is a simple example of what can be done with vinyl-vectors. If you have
-- already cloned the repository, you can run this example with:
--
--   stack build --flag vinyl-vectors:examples && stack exec sorting
--
-- In this example, we build a vector of records. Internally, this is stored
-- as a structure of arrays. We then use the vector-algorithms to sort the
-- data by several different criteria. Feel free to increase the number of
-- elements to a million or change the record type. Have fun.

numberOfElements :: Int
numberOfElements = 20

main :: IO ()
main = do
  -- vector-algoritms only works on mutable vectors,
  -- so we just continually mutate recs throughout
  -- the example.
  recs <- V.thaw exampleVectorData 
  printHeading "Original Data"
  V.mapM_ print =<< V.freeze recs

  Merge.sortBy (compareRecOn (Proxy :: Proxy '[Int])) recs
  printHeading "Sort by Number"
  V.mapM_ print =<< V.freeze recs

  Merge.sortBy (compareRecOn (Proxy :: Proxy '[Char])) recs
  printHeading "Sort by Letter"
  V.mapM_ print =<< V.freeze recs

  Merge.sortBy (compareRecOn (Proxy :: Proxy '[Char,Int])) recs
  printHeading "Sort by Letter, Then by Number"
  V.mapM_ print =<< V.freeze recs

type family RElemAll (rs :: [k]) (ts :: [k]) :: Constraint where
  RElemAll rs '[] = ()
  RElemAll rs (t ': ts) = (RElem t rs (RIndex t rs), RElemAll rs ts)

compareRecOn :: forall rs ts. (RElemAll rs ts, ListAll ts Ord, RecApplicative ts)
  => Proxy ts -> Rec Identity rs -> Rec Identity rs -> Ordering
compareRecOn _ a b = compareRecOnExplicit rpureX a b
  where
  rpureX :: Rec Proxy ts
  rpureX = rpure Proxy 

compareRecOnExplicit :: (RElemAll rs ts, ListAll ts Ord)
  => Rec proxy ts -> Rec Identity rs -> Rec Identity rs -> Ordering
compareRecOnExplicit RNil _ _ = LT -- arbitrary, could be GT as well
compareRecOnExplicit (r :& rs) a b = mappend
  (compare (getIdentity $ a ^. rlens r) (getIdentity $ b ^. rlens r))
  (compareRecOnExplicit rs a b)
  

proxyInt = Proxy :: Proxy Int
proxyChar = Proxy :: Proxy Char
proxyText = Proxy :: Proxy Text

exampleVectorData :: V.Vector (Rec Identity '[Int, Char, Text])
exampleVectorData = V.fromList exampleData

exampleData :: [Rec Identity '[Int, Char, Text]]
exampleData = take numberOfElements $ zipWith3 
  (\i c t -> Identity i :& Identity c :& Identity t :& RNil)
  numbers letters names

letters :: [Char]
letters = scramble (cycle (enumFromTo 'a' 'f'))

numbers :: [Int]
numbers = scramble (enumFrom 1)

names :: [Text]
names = cycle
  ["Drew","Luke","Alexa","Fido","Carlos","Juan"
  ,"Anderson","Shen","Anders", "Andy","Jake"
  ,"Josh","Michelle"
  ]

scramble :: [a] -> [a]
scramble [] = []
scramble (a : b : c : xs) = c : b : a : scramble xs
scramble xs = xs

printHeading :: String -> IO ()
printHeading s = do
  putStrLn "-------------------"
  putStrLn s
  putStrLn "-------------------"