Last active
November 21, 2018 22:10
-
-
Save bradparker/c364699728b64dc74f96786230da01b4 to your computer and use it in GitHub Desktop.
Essence of the iterator pattern word count
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Main where | |
import Data.Bool (bool) | |
import Control.Monad ((<=<)) | |
import Control.Monad.State (State, evalState, get, put) | |
import Data.Char (isSpace) | |
import Data.Foldable (traverse_) | |
import Data.Functor.Compose (Compose(Compose, getCompose)) | |
import Data.Functor.Const (Const(Const, getConst)) | |
import Data.Functor.Product (Product(Pair)) | |
import Data.Maybe (listToMaybe) | |
import Data.Monoid (Sum(Sum, getSum)) | |
import Prelude hiding (lines, words) | |
import System.Environment (getArgs) | |
prod :: (a -> f b) -> (a -> g b) -> a -> Product f g b | |
prod f g a = Pair (f a) (g a) | |
runProd :: (f a -> g a -> b) -> Product f g a -> b | |
runProd f (Pair fa ga) = f fa ga | |
type Count = Const (Sum Integer) | |
getCount :: Count a -> Integer | |
getCount = getSum . getConst | |
count :: Count a | |
count = Const (Sum 1) | |
ignore :: Count a | |
ignore = Const (Sum 0) | |
countWhen :: Bool -> Count a | |
countWhen = bool ignore count | |
chars :: Char -> Count () | |
chars = const count | |
lines :: Char -> Count () | |
lines = countWhen . ('\n' ==) | |
type StatefulCount s = Compose (State s) Count | |
runStatefulCount :: StatefulCount s a -> s -> Integer | |
runStatefulCount c s = getCount (evalState (getCompose c) s) | |
words :: Char -> StatefulCount Bool () | |
words c = | |
Compose $ | |
curry (countWhen . uncurry ((&&) . not)) | |
<$> get | |
<* put (not (isSpace c)) | |
<*> get | |
data WordCount = WordCount | |
{ wcChars :: Integer | |
, wcLines :: Integer | |
, wcWords :: Integer | |
} deriving (Show) | |
wordCount :: String -> WordCount | |
wordCount = | |
runProd (runProd runAll) . traverse (chars `prod` lines `prod` words) | |
where | |
runAll c l w = | |
WordCount (getCount c) (getCount l) (runStatefulCount w False) | |
-- Result: | |
-- $ runhaskell Main.hs ./Main.hs | |
-- WordCount {wcChars = 1857, wcLines = 73, wcWords = 306} | |
main :: IO () | |
main = traverse_ (print . wordCount <=< readFile) =<< listToMaybe <$> getArgs |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment