Skip to content

Instantly share code, notes, and snippets.

@k0001
Last active December 31, 2018 14:45
Show Gist options
  • Save k0001/4500051 to your computer and use it in GitHub Desktop.
Save k0001/4500051 to your computer and use it in GitHub Desktop.
-- | This module holds the messy code I wrote while walking through:
--
-- The Essence of the Iterator Pattern
-- Jeremy Gibbons, Bruno César dos Santos Oliveira. 2009.
-- http://www.cs.ox.ac.uk/publications/publication1409-abstract.html
--
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE UndecidableInstances #-}
-- ^ Some 'Coerce' instances require this.
module Eip where
import Prelude hiding (map, lcm)
import Control.Applicative (Applicative(..))
import Data.Monoid (Monoid(..), (<>), Sum(..))
import Data.Char (isSpace)
import Control.Monad.Trans.Writer
class Bifunctor s where
bimap :: (a -> b) -> (c -> d) -> s a c -> s b d
prop_bimap_identity_law bf =
bimap id id bf == id bf
prop_bimap_composition_law f g h k bf =
bimap (f . h) (g . k) bf == (bimap f g . bimap h k) bf
data Fix s a = In { out :: s a (Fix s a) }
instance Bifunctor s => Functor (Fix s) where
fmap f = In . bimap f (fmap f) . out
fold :: Bifunctor s => (s a b -> b) -> Fix s a -> b
fold f = f . bimap id (fold f) . out
unfold :: Bifunctor s => (b -> s a b) -> b -> Fix s a
unfold f = In . bimap id (unfold f) . f
instance Bifunctor (,) where
bimap f g (a,b) = (f a, g b)
newtype WrappedMonad m a = WrapMonad { unwrapMonad :: m a }
instance Functor m => Functor (WrappedMonad m) where
fmap f = WrapMonad . fmap f . unwrapMonad
instance (Functor m, Monad m) => Applicative (WrappedMonad m) where
pure = WrapMonad . return
mf <*> mx = WrapMonad $ do f <- unwrapMonad mf
x <- unwrapMonad mx
return (f x)
data Stream a = SCons a (Stream a)
deriving (Show)
streamHead (SCons a _) = a
instance Functor Stream where
fmap f (SCons x xs) = SCons (f x) (fmap f xs)
instance Applicative Stream where
pure x = xs where xs = SCons x xs
(SCons f fs) <*> (SCons x xs) = SCons (f x) (fs <*> xs)
newtype Const b a = Const { unConst :: b }
deriving (Show)
instance Functor (Const b) where
fmap _ (Const x) = Const x
instance Monoid b => Applicative (Const b) where
pure _ = Const mempty
(Const x) <*> (Const y) = Const (x `mappend` y)
-- instance Monoid b => Monoid (Const b a) where
-- mempty = Const mempty
-- mappend (Const x) (Const y) = Const (x `mappend` y)
newtype Reader r a = R { runReader :: r -> a }
deriving (Functor, Monad, Applicative)
ask :: Reader r r
ask = R $ \r -> r
{- Stuff
-
instance Functor (Reader r) where
fmap f (R g) = R (f . g)
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
instance Applicative (Reader r) where
pure x = R (const x)
-- (Reader ( a -> b)) <*> (Reader a ) = Reader b
-- (R (r -> (a -> b)) <*> (R (r -> a)) = R (r -> b)
(R f ) <*> (R x ) = R (\r -> f r (x r))
instance Functor ((->) r) where
-- fmap :: (a -> b) -> F a -> F b
-- fmap :: (a -> b) -> (->) r a -> (->) r b
-- fmap :: (a -> b) -> (r -> a) -> (r -> b)
fmap f g = \r -> (f . g) r
instance Applicative ((->) r) where
-- pure :: a -> A a
-- pure :: a -> (->) r a
-- pure :: a -> (r -> a)
pure x = const x
-- (<*>) :: A (a -> b) -> A a -> A b
-- (<*>) :: (->) r (a -> b) -> (->) r a -> (->) r b
-- (<*>) :: (r -> (a -> b)) -> (r -> a) -> (r -> b)
(<*>) f g = \r -> f r (g r)
instance Monad ((->) r) where
-- return :: a -> M a
-- return :: a -> (->) r a
-- return :: a -> (r -> a)
return x = \r -> x
-- (>>=) :: M a -> (a -> M b) -> M b
-- (>>=) :: (->) r a -> (a -> (->) r b) -> (->) r b
-- (>>=) :: (r -> a) -> (a -> (r -> b)) -> (r -> b)
(>>=) f k = \r -> k (f r) r
-}
data Product m n a = Product { pfst :: m a, psnd :: n a }
deriving (Show)
prod :: (Functor m, Functor n) => (a -> m b) -> (a -> n b) -> (a -> Product m n b)
prod f g x = Product (f x) (g x)
instance (Functor m, Functor n) => Functor (Product m n) where
fmap f (Product a b) = Product (fmap f a) (fmap f b)
instance (Applicative m, Applicative n) => Applicative (Product m n) where
pure x = Product (pure x) (pure x)
mf <*> mx = Product (pfst mf <*> pfst mx) (psnd mf <*> psnd mx)
data Compose m n a = Compose { unCompose :: m (n a) }
deriving (Show)
comp :: (Functor m, Functor n) => (b -> n c) -> (a -> m b) -> (a -> Compose m n c)
comp f g x = Compose (fmap f (g x))
instance (Functor m, Functor n) => Functor (Compose m n) where
fmap f (Compose x) = Compose (fmap (fmap f) x)
instance (Applicative m, Applicative n) => Applicative (Compose m n) where
pure x = Compose (pure (pure x))
mf <*> mx = Compose (pure (<*>) <*> unCompose mf <*> unCompose mx)
traverseList :: Applicative m => (a -> m b) -> [a] -> m [b]
traverseList f [] = pure []
traverseList f (x:xs) = pure (:) <*> f x <*> traverseList f xs
distList :: Applicative m => [m a] -> m [a]
distList = traverseList id
class Functor t => Traversable t where
traverse :: Applicative m => (a -> m b) -> t a -> m (t b)
traverse f = dist . fmap f
-- Free theorems:
-- 1. traverse (g . h) = traverse g . fmap h
-- 2. traverse (fmap k . f) = fmap (fmap k) . traverse f
dist :: Applicative m => t (m a) -> m (t a)
dist = traverse id
-- Free theorem:
-- 1. dist . fmap (fmap k) = fmap (fmap k) . dist
data Tree a = Leaf a | Bin (Tree a) (Tree a)
deriving (Show, Eq)
sampleTree :: Tree Int
sampleTree = Bin (Bin (Leaf 3) (Bin (Bin (Leaf 5) (Leaf 3)) (Bin (Leaf 9) (Bin (Bin (Leaf 8) (Leaf 1)) (Leaf 2))))) (Leaf 4)
instance Functor Tree where
fmap f (Leaf a) = Leaf (f a)
fmap f (Bin l r) = Bin (fmap f l) (fmap f r)
instance Traversable Tree where
traverse f (Leaf x) = pure Leaf <*> f x
traverse f (Bin l r) = pure Bin <*> traverse f l <*> traverse f r
class Bifunctor s => Bitraversable s where
bdist :: Applicative m => s (m a) (m b) -> m (s a b)
instance Bitraversable s => Traversable (Fix s) where
traverse f = fold (fmap In . bdist . bimap f id)
instance (Traversable m, Traversable n) => Traversable (Product m n) where
traverse f (Product mx nx) = pure Product <*> traverse f mx <*> traverse f nx
newtype Id a = Id { unId :: a }
deriving (Show)
instance Traversable Id where
traverse f (Id x) = pure Id <*> f x
instance Functor Id where
fmap f (Id x) = Id (f x)
instance Applicative Id where
pure = Id
Id f <*> Id x = Id (f x)
instance Traversable [] where
traverse _ [] = pure []
traverse f (x:xs) = pure (:) <*> f x <*> traverse f xs
-- Data.Traversable.foldMapDefault
-- :: (Traversable t, Monoid m) => (a -> m) -> t a -> m
reduce :: (Traversable t, Monoid m) => (a -> m) -> t a -> m
reduce f = unConst . traverse (Const . f)
-- Data.Foldable.fold
-- :: (Foldable t, Monoid m) => t m -> m
crush :: (Traversable t, Monoid m) => t m -> m
crush = reduce id
instance Traversable Stream where
traverse f (SCons x xs) = pure SCons <*> f x <*> traverse f xs
class Coerce a b | a -> b where
d' :: a -> b
c' :: b -> a
instance Coerce (Id a) a where
d' = unId
c' = Id
instance Coerce (Const a b) a where
d' = unConst
c' = Const
-- {- The following 3 instances require enabling UndecidableInstances
instance (Functor m, Functor n, Coerce (m a) b, Coerce (n a) c) => Coerce (Product m n a) (b, c) where
d' mnx = (d' (pfst mnx), d' (psnd mnx))
c' (x,y) = Product (c' x) (c' y)
instance (Functor m, Functor n, Coerce (m b) c, Coerce (n a) b) => Coerce (Compose m n a) c where
d' = d' . fmap d' . unCompose
c' = Compose . fmap c' . c'
instance (Monad m, Coerce (m a) c) => Coerce (WrappedMonad m a) c where
d' = d' . unwrapMonad
c' = WrapMonad . c'
-- -}
contentsBody :: a -> Const [a] b
contentsBody x = Const [x]
contents :: Traversable t => t a -> Const [a] (t b)
contents = traverse contentsBody
run :: (Coerce b c, Traversable t) => (t a -> b) -> t a -> c
run program = d' . program
runContents :: Traversable t => t a -> [a]
runContents = run contents
shapeBody :: a -> Id ()
shapeBody _ = Id ()
shape :: Traversable t => t a -> Id (t ())
shape = traverse shapeBody
runShape :: Traversable t => t a -> t ()
runShape = run shape
-- Decompose into shape and contents using parallel traversals.
decompose :: Traversable t => t a -> Product Id (Const [a]) (t ())
decompose = prod shape contents
-- decompose x = Product (shape x) (contents x)
-- Decompose into shape and contents fusing traversals.
decompose' :: Traversable t => t a -> Product Id (Const [a]) (t ())
decompose' = traverse $ prod shapeBody contentsBody
instance Coerce (Maybe a) (Maybe a) where
d' = id
c' = id
newtype State s a = State { runState :: s -> (a,s) }
instance Functor (State s) where
fmap f x = State $ \s -> let (a, s') = runState x s
in (f a, s')
instance Monad (State s) where
return a = State $ \s -> (a, s)
m >>= k = State $ \s -> let (a, s') = runState m s
in runState (k a) s'
-- get :: State s s
-- get = State $ \s -> (s, s)
--
-- put :: s -> State s ()
-- put s = State $ \s' -> ((), s)
squareInCubeState :: Int -> State Int Int
squareInCubeState z = do
-- Triple cube state. We do it in two steps to check if 'get' and
-- 'put' work as expected.
x <- get; put (x * x)
xx <- get; put (x * xx)
-- Double out
return (z * z)
prop_squareInCubeState a s
= (a*a, s*s*s) == runState (squareInCubeState a) s
instance Coerce (State s a) (s -> (a,s)) where
d' = runState
c' = State
-- {- mindblowing stuff
reassembleBody :: () -> Compose (WrappedMonad (State [a])) (WrappedMonad Maybe) a
reassembleBody = c' . takeHead
where takeHead _ [] = (Nothing, [])
takeHead _ (x:xs) = (Just x , xs)
reassemble :: Traversable t => t () -> Compose (WrappedMonad (State [a])) (WrappedMonad Maybe) (t a)
reassemble = traverse reassembleBody
runReassemble :: Traversable t => (t (), [a]) -> Maybe (t a)
runReassemble = fst . uncurry (run reassemble)
prop_reassemble t =
let decomposed = d' $ decompose t
in maybe False (==t) $ runReassemble decomposed
-- try: True == prop_reassemble sampleTree
-- -} end of mindblowing stuff
-- perform the effect @f@ for each @a@ and purely map values using @g@.
collect :: (Traversable t, Applicative m) => (a -> m ()) -> (a -> b) -> t a -> m (t b)
collect f g = traverse (\a -> pure (\() -> g a) <*> f a)
-- traverse the structure using the State monad to capture counting and
-- purely mapping values with @f@
loop :: Traversable t => (a -> b) -> t a -> WrappedMonad (State Int) (t b)
loop f = collect (\a -> WrapMonad (get >>= put . succ)) f
loop' :: Traversable t => (a -> b) -> Int -> t a -> (t b, Int)
loop' f s t = runState (unwrapMonad $ loop f t) s
-- loop' odd 10 [1,2,3,4,5] == ([True,False,True,False,True],15)
-- Super cool. Apply @g@ to each @a@ from @ta@ and @b@ from the effectful @mb@.
disperse :: (Traversable t, Applicative m) => m b -> (a -> b -> c) -> t a -> m (t c)
disperse mb g ta = traverse (\a -> pure (g a) <*> mb) ta
-- Almost the same as above but taking an applicative action.
dispersek :: (Traversable t, Applicative m) => (a -> m b) -> (a -> b -> c) -> t a -> m (t c)
dispersek kb g = traverse (\a -> fmap (g a) (kb a))
-- dispersek (\a -> putStrLn ("??? "++a) >> getLine) (++) ["Hi ", "Bye "]
-- ??? Hi
-- World
-- ??? Bye
-- bdf
-- ["Hi World","Bye bdf"]
-- Label each element with its position in order of traversal
label :: Traversable t => t a -> WrappedMonad (State Int) (t Int)
label = disperse (WrapMonad labelStep) (\_ b -> b)
labelStep :: State Int Int
labelStep = get >>= \n -> put (n+1) >> return n
labelStepA :: a -> WrappedMonad (State Int) Int
labelStepA _ = WrapMonad labelStep
label' :: Traversable t => t a -> Id (t Int)
label' = c' . fst . flip runState 0 . unwrapMonad . label
-- label' [8,6,7] == [0,1,2]
-- d' $ prod label' contents (Just 4) == (Just 0,[4])
instance Traversable Maybe where
traverse f Nothing = pure Nothing
traverse f (Just x) = pure Just <*> f x
newtype Backwards m a = Backwards { runBackwards :: m a }
deriving (Show)
instance Functor f => Functor (Backwards f) where
fmap f (Backwards mx) = Backwards $ fmap f mx
instance Applicative m => Applicative (Backwards m) where
pure = Backwards . pure
mf <*> mx = Backwards $ pure (flip ($)) <*> runBackwards mx <*> runBackwards mf
data AppAdapter m where
AppAdapter :: Applicative (g m)
=> (forall a. m a -> g m a)
-> (forall a. g m a -> m a)
-> AppAdapter m
backwards :: Applicative m => AppAdapter m
backwards = AppAdapter Backwards runBackwards
ptraverse :: (Applicative m, Traversable t) => AppAdapter m -> (a -> m b) -> t a -> m (t b)
ptraverse (AppAdapter c d) f = d . traverse (c . f)
revContents :: Traversable t => t a -> Const [a] (t b)
revContents = ptraverse backwards contentsBody
newtype Forwards m a = Forwards { runForwards :: m a }
instance Functor m => Functor (Forwards m) where
fmap f (Forwards mx) = Forwards $ fmap f mx
instance Applicative m => Applicative (Forwards m) where
pure = Forwards . pure
mf <*> mx = Forwards $ runForwards mf <*> runForwards mx
forwards :: Applicative m => AppAdapter m
forwards = AppAdapter Forwards runForwards
-- [4,3,2] == d' $ comp (ptraverse backwards contentsBody) (traverse (\a -> Id (a+1))) [1,2,3]
-- Given an applicative functor transformation 'f':
-- f :: (Applicative m, Applicative n) => m a -> n a
--
-- - 'f' is an homomorphism over the structure of applicative, functors respecting:
-- 1. f (pure_m a) = pure_n a
-- 2. f (mf <*>_m mx) = (f mf) <*>_n (f mx)
--
-- - 'dist' satisfies the following naturality property:
-- 1. dist_n . fmap f = f . dist_m
--
-- From this, the Purity Law follows:
-- a. traverse pure = pure
--
-- An f a Fusion Law for the parallel composition of traversals:
-- b. (traverse f `prod` traverse g) = traverse (f `prod` g)
{- Preserving Purity Law
| This definition of traverse in which the two children are swapped on
traversal breaks the Purity Law.
instance Traversable Tree where
traverse f (Leaf x) = pure Leaf <*> f x
traverse f (Bin l r) = pure Bin <*> traverse f r <*> traverse f l
| This definition of traverse doesn't break the Purity Law. The two children are
swapped on traversal but the 'Bin' operater is flipped to compensate. The effect
of the reversal is that the elements of the tree are traversed from right to
left.
instance Traversable Tree where
traverse f (Leaf x) = pure Leaf <*> f x
traverse f (Bin l r) = pure (flip Bin) <*> traverse f r <*> traverse f l
-- Btw, we can use 'Backwards' to achieve the same reversed traversal.
-}
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)
(<=<) fc fb a = fb a >>= fc
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
(>=>) = flip (<=<)
prop_traverse_kleisli_fuse kc kb ta = tc == tc'
where tc = (traverse kc <=< traverse kb) ta
tc' = traverse (kc <=< kb) ta
-- True == prop_traverse_kleisli_fuse (\b -> Just (b*10)) (\a -> Just (a+1)) [1,2,3]
ap :: Monad m => m (a -> b) -> m a -> m b
ap mf ma = do { f <- mf; a <- ma; return (f a) }
instance Applicative (State s) where
pure x = State $ \s -> (x, s)
(<*>) = ap
update1, update2 :: a -> State Int a
update1 x = do { s <- get; put (s*2); return x }
update2 x = do { s <- get; put (s+1); return x }
-- monadic1 /= monadic2.
-- Composing some Kleisli arrows, such as update1 and update2, is not
-- necessarily commutative.
monadic1, monadic2 :: Traversable t => t a -> State Int (t a)
monadic1 = traverse update1 <=< traverse update2
monadic2 = traverse (update1 <=< update2)
-- applicative1 == applicative2
applicative1, applicative2 :: Traversable t => t a -> Compose (State Int) (State Int) (t a)
applicative1 = comp (traverse update1) (traverse update2)
applicative2 = traverse (comp update1 update2)
-- XXX I'm not sure if this is OK.
runA12 is1 is2 ma ta = ((is1, is2, s1), (a1, a1'))
where
st1 = unCompose $ ma ta
(st1', a1) = runState st1 is1
(a1', s1) = runState st1' is2
-- XXX and this
prop_applicative12 :: (Eq (t a), Traversable t) => Int -> Int -> t a -> Bool
prop_applicative12 is1 is2 ta = xx1 == xx2
where xx1 = runA12 is1 is2 applicative1 ta
xx2 = runA12 is1 is2 applicative2 ta
index :: Traversable t => t a -> (t Int, Int)
index xs = run label xs 0
{- This traversal visits the same element multiple times. It satisfies the
purity law. However, if behaves strangely in cases such as extracting a list of
element indices:
instance Traversable [] where
traverse f [] = pure []
traverse f (x:xs) = pure (const (:)) <*> f x <*> f x <*> traverse f xs
indices are not quite expected:
([1,3,5],6) == index [1,2,3]
nor contents:
[1,1,2,2,3,3] == runContents [1,2,3]
even while:
m [1,2,3] == traverse pure [1,2,3]
-}
{- Word Count -}
-- Accumulate a result in the Int-as-Monoid applicative functor:
type Count = Const (Sum Int)
count :: Int -> Count b
count n = Const (Sum n)
-- The body of the iteration simply yields 1 for every element:
cciBody :: Char -> Count a
cciBody _ = count 1
-- Traversing with 'cciBody' accumulates the character count
cci :: String -> Count [a]
cci = traverse cciBody
test :: Bool -> Int
test b = if b then 1 else 0
-- Yield 1 for every line
lciBody :: Char -> Count a
lciBody c = count $ test (c == '\n')
-- Traversing with 'lciBody' accumulates the line count
lci :: String -> Count [a]
lci = traverse lciBody
-- Word count
wciBody :: Char -> Compose (WrappedMonad (State Bool)) Count a
wciBody c = comp Const (WrapMonad . State) updateState
where updateState w = let s = not (isSpace c)
in (Sum $ test (not w && s), s)
wci :: String -> Compose (WrappedMonad (State Bool)) Count [a]
wci = traverse wciBody
runWci :: String -> Int
runWci s = getSum $ fst (run wci s False)
-- Count characters and lines sequentially.
clci' :: String -> Product Count Count [a]
clci' = prod cci lci
-- Count characters and lines in parallel.
clci :: String -> Product Count Count [a]
clci = traverse (prod cciBody lciBody)
-- Count characers, lines and words in parallel.
cclwi :: String -> Product (Product Count Count) (Compose (WrappedMonad (State Bool)) Count) [a]
cclwi = traverse (cciBody `prod` lciBody `prod` wciBody)
-- clwci = traverse (prod (prod cciBody lciBody) wciBody)
runCclwi :: String -> (Int {- chars -}, Int {- lines -}, Int {- words -})
runCclwi s = uncurry f (run cclwi s) where
f (Sum cc, Sum lc) wck = let (Sum wc, _) = wck False
in (cc, lc, wc)
-- (34,3,7) == runCclwi "one two\n three four\nfive six\nseven"
quiBody :: Char -> Pair Bool
quiBody c = Pair (c == 'q', c =='u')
qui :: String -> Pair [Bool]
qui = traverse quiBody
newtype Pair a = Pair (a,a)
deriving (Show, Eq)
instance Functor Pair where
fmap f (Pair (a,a')) = Pair (f a, f a')
instance Applicative Pair where
pure a = Pair (a,a)
Pair (f,f') <*> Pair (a,a') = Pair (f a, f' a')
instance Coerce (Pair a) (a,a) where
d' (Pair x) = x
c' = Pair
-- qui "aqua" == Pair ([False,True,False,False],[False,False,True,False])
ccqui :: String -> Product Count Pair [Bool]
ccqui = traverse $ cciBody `prod` quiBody
-- run ccqui "qui asd"
-- == (Sum {getSum = 7},([True,False,False,False,False,False,False]
-- ,[False,True,False,False,False,False,False]))
wcqui :: String -> Product Pair (Compose (WrappedMonad (State Bool)) Count) [Bool]
wcqui = traverse $ quiBody `prod` wciBody
-- In general component traversals may not be so amenable to composition as in
-- 'wcqui', and Product may not be the appropiate combinator. Here we use
-- Compose instead.
wcqui' :: String -> Compose (Product Id (Compose (WrappedMonad (State Bool)) Count)) Pair [Bool]
wcqui' = traverse (quiBody `comp` (Id `prod` wciBody))
runWcqui :: String -> (([Bool], [Bool]), Int)
runWcqui = fmap (\wks -> getSum . fst . wks $ False) . run wcqui
runWcqui' :: String -> (([Bool], [Bool]), Int)
runWcqui' = fmap (\wks -> getSum . fst . wks $ False) . run wcqui'
-- Now count characters, lines and words using Writer.
ccmBody :: Char -> Writer (Sum Int) Char
ccmBody c = tell (Sum 1) >> return c
ccm :: String -> Writer (Sum Int) String
ccm = traverse ccmBody
lcmBody :: Char -> Writer (Sum Int) Char
lcmBody c = tell (Sum $ test (c == '\n')) >> return c
lcm :: String -> Writer (Sum Int) String
lcm = traverse lcmBody
wcmBody :: Char -> State (Int, Bool) Char
wcmBody c = do
let s = not (isSpace c)
(n,w) <- get
put (n + (test (not w && s)), s)
return c
wcm :: String -> State (Int, Bool) String
wcm = traverse wcmBody
runWcm :: String -> Int
runWcm ta = wc where
(_,(wc,_)) = runState (wcm ta) (0,False)
-- Now fuse the three monadic traversals into one.
clwcm, clwcm'
:: String
-> Product (Product (Writer (Sum Int)) (Writer (Sum Int)))
(State (Int, Bool))
String
-- sequentially:
clwcm = ccm `prod` lcm `prod` wcm
-- in parallel:
clwcm' = traverse $ ccmBody `prod` lcmBody `prod` wcmBody
runClwcm' :: String -> (Int {- chars -}, Int {- lines -}, Int {- words -})
runClwcm' s = (cc, lc, wc) where
mwcc `Product` mwlc `Product` mswc = clwcm' s
(_,Sum cc) = runWriter mwcc
(_,Sum lc) = runWriter mwlc
(_,(wc,_)) = runState mswc (0,False)
data Qum = QumU | QumQ deriving (Show, Eq)
qumBody :: Char -> Reader Qum Bool
qumBody c = ask >>= return . f where
f QumQ = (c == 'q')
f QumU = (c == 'u')
qum :: String -> Reader Qum [Bool]
qum = traverse qumBody
class MonadTrans t where
lift :: Monad m => m a -> t m a
newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
instance Monad m => Monad (StateT s m) where
return a = StateT $ \s -> return (a,s)
m >>= k = StateT $ \s -> do
(a, s') <- runStateT m s
runStateT (k a) s'
instance MonadTrans (StateT s) where
lift ma = StateT $ \s -> ma >>= \a -> return (a,s)
class Monad m => MonadState s m | m -> s where
get :: m s
put :: s -> m ()
instance MonadState s (State s) where
get = State $ \s -> (s, s)
put s = State $ \s' -> ((), s)
instance Monad m => MonadState s (StateT s m) where
get = StateT $ \s -> return (s, s)
put s = StateT $ \s' -> return ((), s)
(<=<^) :: (Monad m, MonadTrans t, Monad (t m))
=> (b -> t m c) -> (a -> m b) -> (a -> t m c)
ktf <=<^ kg = ktf <=< (lift . kg)
(^<=<) :: (Monad m, MonadTrans t, Monad (t m))
=> (b -> m c) -> (a -> t m b) -> (a -> t m c)
kf ^<=< ktg = (lift . kf) <=< ktg
wcmBody' :: MonadState (Int,Bool) m => Char -> m Char
wcmBody' c = do
let s = not (isSpace c)
(n,w) <- get
put (n + (test (not w && s)), s)
return c
quwcm :: String -> StateT (Int, Bool) (Reader Qum) [Bool]
quwcm = mapM (qumBody ^<=< wcmBody') -- == mapM qumBody ^<=< mapM wcmBody'
runQuwcm :: String -> Qum -> ([Bool], Int)
runQuwcm ta qx = (qu, wc) where
(qu,(wc,_)) = runReader (runStateT (quwcm ta) (0, False)) qx
{- Abstracting with Applicatives.
Starting from here, code is related to Edward Kmett's article at
http://comonad.com/reader/2012/abstracting-with-applicatives/
-}
type WriterA b = Product (Const b) Id
tellA :: b -> WriterA b ()
tellA x = Product (Const x) (pure ())
-- >>> tellA [1,2] *> tellA [2]
-- Product {pfst = Const {unConst = [1,2,2]}, psnd = Id {unId = ()}}
type FailingWriterA b = Compose (WriterA b) Maybe
tellFWA :: Monoid b => b -> FailingWriterA b ()
tellFWA x = Compose (tellA x *> pure (Just ()))
-- >>> tellFWA [1,2] *> tellFWA [3]
-- Compose {unCompose = Product {pfst = Const {unConst = [1,2,3]},
-- psnd = Id {unId = Just ()}}}
failFWA :: Monoid b => FailingWriterA b a
failFWA = Compose (pure Nothing)
-- >>> tellFWA [1,2] *> failFWA *> tellFWA [3]
-- Compose {unCompose = Product {pfst = Const {unConst = [1,2,3]},
-- psnd = Id {unId = Nothing}}}
justFWA :: Monoid b => a -> FailingWriterA b a
justFWA x = Compose (pure (Just x))
-- >>> tellFWA [1,2] *> justFWA (*5) <*> justFWA 4
-- Compose {unCompose = Product {pfst = Const {unConst = [1,2]},
-- psnd = Id {unId = Just 20}}}
-- >>> tellFWA [1,2] *> justFWA (*5) <*> failFWA
-- Compose {unCompose = Product {pfst = Const {unConst = [1,2]},
-- psnd = Id {unId = Nothing}}}
type HasEnv k m = Product (Const [k]) m
takeEnv :: (k -> m a) -> k -> HasEnv k m a
takeEnv f x = Product (Const [x]) (f x)
-- >>> let he = takeEnv (\e k -> e + k) 5
-- >>> :t he
-- he :: HasEnv Integer ((->) Integer) Integer
-- >>> pfst he
-- Const {unConst = [5]}
-- >>> :t psnd he
-- psnd he :: Integer -> Integer
-- >>> psnd he 10
-- 15
takeEnvNew :: (e -> k -> a) -> k -> HasEnv k (Reader e) a
takeEnvNew f x = Product (Const [x]) (R $ flip f x)
-- >>> let hen = takeEnvNew (\e k -> e + k) 5
-- >>> :t hen
-- hen :: HasEnv Integer (Reader Integer) Integer
-- >>> pfst hen
-- Const {unConst = [5]}
-- >>> :t psnd hen
-- psnd he :: Reader Integer Integer
-- >>> runReader (psnd hen) 10
-- 15
-- | Something that can be executed in one context as a monoid that
-- builds a form, and in another as a parser.
type FormletOne b a
= Product (Const b) Id a
-- | .. also we can read from and environment and perhaps get an answer.
type FormletTwo b env a
= Product (Const b) (Compose (Reader env) Maybe) a
-- | .. also we can get monoidal trace of errors wether we succeed or not.
type FormletThree b err env a
= Product (Const b) (Compose (Reader env) (Product (Const err) Maybe)) a
-- ... but we want to either succeed, or get errors. So we need a sum
-- functor:
data Coproduct f g a = InL (f a) | InR (g a) deriving Show
-- ^ Named 'Coproduct' in comonad-transformers.
instance (Functor f, Functor g) => Functor (Coproduct f g) where
fmap f (InL x) = InL (fmap f x)
fmap g (InR y) = InR (fmap g y)
-- To make @'Coproduct' f g@ we need some sort of “bias”, since 'pure'
-- should prefer to inject the value to just one of 'InL' and 'InR'. We
-- pick 'InR' arbitrarily, following 'Either'\'s convention of using
-- 'Right'.
--
-- We need the capacity to “work in” one side of the sum until compelled
-- to switch over to the other, at which point we are stuck there.
--
-- We can make @'Coproduct' f g@ an 'Applicative'as long as there is a
-- natural transformation between @g@ and @f@, that is, there is a
-- function:
--
-- eta :: (Functor f, Functor g) g x -> f x
--
-- such that 'eta' “respects fmap”:
--
-- fmap h . eta == eta . fmap h
--
-- (Actually we want something stronger, called a “monoidal natural
-- transformation” that respects not only 'fmap', but '<*>' too.)
class Natural f g where
eta :: f a -> g a
instance (Applicative f, Applicative g, Natural g f)
=> Applicative (Coproduct f g) where
pure x = InR (pure x)
InR g <*> InR y = InR (g <*> y)
InL f <*> InL x = InL (f <*> x)
InL f <*> InR x = InL (f <*> eta x)
InR g <*> InL x = InL (eta g <*> x)
-- | Terminal homomorphism that sends any functor to 'Const':
instance Monoid b => Natural f (Const b) where
eta = const (Const mempty)
instance Applicative f => Natural g (Compose f g) where
eta = Compose . pure
instance (Applicative g, Functor f) => Natural f (Compose f g) where
eta = Compose . fmap pure
instance (Natural f g) => Natural f (Product f g) where
eta fa = Product fa (eta fa)
instance (Natural g f) => Natural g (Product f g) where
eta ga = Product (eta ga) ga
instance Natural (Product f g) f where
eta (Product fa _) = fa
instance Natural (Product f g) g where
eta (Product _ ga) = ga
instance Natural g f => Natural (Coproduct f g) f where
eta (InL fa) = fa
eta (InR ga) = eta ga
instance Natural Id (Reader r) where
eta (Id x) = R (pure x)
-- To avoid overlapping issues, we can't both have the terminal
-- homomorphism that sends everything to 'Const' and the initial
-- homomorphism that sends 'Id' to anything like so:
--
-- instance Applicative g => Natural Id g where
-- eta (Id x) = pure x
--
-- A version of 'Coproduct' with the initial tranformation baked in
-- lives in 'transformers' as 'Lift'.
-- | This applicative will yield either a single result 'InR', or an
-- accumulation of monoidal errors 'InL'. It exists on hackage in the
-- 'Validation' package.
type Validation b = Coproduct (Const b) Id
validationError :: Monoid b => b -> Validation b a
validationError x = InL (Const x)
-- Now we can produce a full formlet that does what we want:
--
-- Something that can be executed in one context as a monoid @b@ that
-- builds a form, and in another as a parser that can read from an
-- environment @env@ an either succeed providing a result @a@ or fail providing
-- a trace of errors @err@.
type Formlet b err env a
= Product (Const b)
(Compose (Reader env) (Coproduct (Const err) Id))
a
-- Stripping away all the above noise:
type FormletClean b err env a = (b, env -> Either err a)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment