Last active
September 1, 2017 19:25
-
-
Save gatlin/c5556f4d09a42f4c665f to your computer and use it in GitHub Desktop.
Hunter S Thompson once typed out the entirety of "The Great Gatsby" so that he could feel what it was like to write the great American novel. In this spirit I am reimplementing core concepts in functional programming and trying to work out the harder puzzles myself.
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
{-# LANGUAGE RankNTypes #-} | |
{-# LANGUAGE MultiParamTypeClasses #-} | |
{-# LANGUAGE FunctionalDependencies #-} | |
{-# LANGUAGE FlexibleInstances #-} | |
{-# LANGUAGE UndecidableInstances #-} | |
{-# LANGUAGE FlexibleContexts #-} | |
{-# LANGUAGE NoMonomorphismRestriction #-} | |
{-# LANGUAGE DeriveFunctor #-} | |
{-# LANGUAGE StandaloneDeriving #-} | |
{-# LANGUAGE ExistentialQuantification #-} | |
{-# LANGUAGE GeneralizedNewtypeDeriving #-} | |
{-# LANGUAGE AllowAmbiguousTypes #-} | |
-- The following functions are re-implemented | |
import Prelude hiding ( filter | |
, sequence | |
, sequence_ | |
, mapM_ | |
, maybe | |
, foldr | |
, foldl | |
, map | |
, concat | |
, (++) | |
, unzip | |
, iterate | |
, length | |
, empty | |
, Monoid | |
, succ | |
, replicate | |
, take | |
, repeat | |
, Either | |
, either | |
, Left | |
, Right | |
, join | |
, zip | |
, zipWith | |
, reverse | |
, unlines | |
) | |
-- Pragmatic utilities for displaying values in the console and making some | |
-- slightly more interesting examples | |
import qualified Prelude as P (concat, (++), length, foldr, filter) | |
import System.IO (isEOF) | |
import Data.Char (toUpper) | |
-- Machinery Haskell expects me to use (for example, any monad instances I | |
-- define must also be Applicative instances; can't have one without the | |
-- other). | |
import Control.Applicative hiding ( some | |
, getConst | |
, Const | |
, empty | |
, Alternative(..) | |
) | |
import Control.Monad.Trans.Class | |
{- | |
- Prologue: typeclass trickery and building blocks | |
- | |
- The Functor and Monad typeclasses are two of the most pervasive and useful | |
- typeclasses in Haskell. Functor looks like this: | |
- | |
- class Functor f where | |
- fmap :: (a -> b) -> f a -> f b | |
- (<$) :: a -> f b -> f a | |
- (<$) = fmap . const | |
- | |
- Monad looks like this: | |
- | |
- class Monad m where | |
- return :: a -> m a | |
- (>>=) :: forall a b. m a -> (a -> m b) -> m b | |
- (>>) :: forall a b. m a -> m b -> m b | |
- mv >> f = mv >>= \_ -> f | |
- fail :: String -> m a | |
- fail s = error s | |
- | |
- Were I truly implementing the language from scratch these wouldn't be | |
- commented out. However re-implementing them would do nothing for me | |
- educationally and would make working with the rest of Haskell really | |
- annoying. | |
- | |
- For completion, here is the definition of Applicative, which is a functor | |
- which may be used to perform a number of actions in a sequence and collect | |
- the results. Since I'm importing Haskell's monad class, I need to also use | |
- its Applicative class. | |
- | |
- class Functor f => Applicative f where | |
- pure :: a -> f a | |
- (<*>) :: f (a -> b) -> f a -> f b | |
- | |
-} | |
-- | The identity functor | |
newtype Box a = Box { unbox :: a } deriving Show | |
instance Functor Box where | |
fmap f i = Box $ f $ unbox i | |
instance Foldable Box where | |
foldMap f (Box x) = f x | |
instance Applicative Box where | |
pure a = Box a | |
Box f <*> Box x = Box (f x) | |
instance Monad Box where | |
return a = Box a | |
m >>= k = k (unbox m) | |
instance Comonad Box where | |
duplicate = Box | |
{-# INLINE duplicate #-} | |
extract = unbox | |
{-# INLINE extract #-} | |
newtype Const a b = Const { getConst :: a } | |
instance Functor (Const a) where | |
fmap _ (Const x) = Const x | |
newtype Pair a b = Pair { unpair :: forall r. (a -> b -> r) -> r } | |
instance Functor (Pair a) where | |
fmap f p = unpair p (\x y -> pair x (f y)) | |
pair :: a -> b -> Pair a b | |
pair x y = Pair $ \f -> f x y | |
fst' :: Pair r b -> r | |
fst' p = unpair p (\f _ -> f) | |
snd' :: Pair a r -> r | |
snd' p = unpair p (\_ s -> s) | |
{- | |
- Bifunctors | |
- | |
- A functor which is parameterized over two variables | |
-} | |
class Bifunctor p where | |
bimap :: (a -> b) -> (c -> d) -> p a c -> p b d | |
bimap f g = bifirst f . bisecond g | |
bifirst :: (a -> b) -> p a c -> p b c | |
bifirst f = bimap f id | |
bisecond :: (b -> c) -> p a b -> p a c | |
bisecond = bimap id | |
instance Bifunctor (,) where | |
bimap f g ~(a, b) = (f a, g b) | |
instance Bifunctor Const where | |
bimap f _ (Const a) = Const (f a) | |
{-# INLINE bimap #-} | |
{- | |
- Monoids | |
- | |
- A monoidal type is a type with: | |
- | |
- 1. An associative binary operation; and | |
- 2. An identity for this operation | |
- | |
- A list can be thought of as the free monoid: it can wrap any type - monoid | |
- or not - and produce a type which is a monoid. | |
-} | |
class Monoid a where | |
empty :: a | |
append :: a -> a -> a | |
mconcat :: List a -> a | |
mconcat xs = listFoldr xs append empty | |
(<>) :: Monoid a => a -> a -> a | |
(<>) = append | |
instance Monoid b => Monoid (a -> b) where | |
empty _ = empty | |
append f g x = f x `append` g x | |
instance Monoid String where | |
empty = "" | |
append x y = x P.++ y | |
-- | The monoid of endomorphisms under composition | |
newtype Endo a = Endo { appEndo :: a -> a } | |
instance Monoid (Endo a) where | |
empty = Endo id | |
Endo f `append` Endo g = Endo (f . g) | |
-- | The dual of a monoid, by swapping the arguments of append | |
newtype MonoidDual a = MonoidDual { getMonoidDual :: a } | |
instance Monoid a => Monoid (MonoidDual a) where | |
empty = MonoidDual empty | |
MonoidDual x `append` MonoidDual y = MonoidDual (y `append` x) | |
{- | | |
- Alternative | |
- | |
- An Alternative is a sub-type of Applicative which are also monoids. | |
-} | |
class Applicative f => Alternative f where | |
zilch :: f a | |
(<|>) :: f a -> f a -> f a | |
class Functor f => Alt f where | |
(<!>) :: f a -> f a -> f a | |
some :: Applicative f => f a -> f (List a) | |
some v = some_v where | |
many_v = some_v <!> pure nil | |
some_v = cons <$> v <*> many_v | |
many :: Applicative f => f a -> f (List a) | |
many v = many_v where | |
many_v = some_v <!> pure nil | |
some_v = cons <$> v <*> many_v | |
{- | | |
- Foldable | |
- | |
- A foldable type is one which can be folded (surprise). | |
- A minimal complete definition: `foldMap` or `foldr`. | |
-} | |
class Foldable t where | |
-- | Combine the elements of a structure using a monoid | |
fold :: Monoid m => t m -> m | |
fold = foldMap id | |
-- | Map each element of the structure to a monoid, and combine the results | |
foldMap :: Monoid m => (a -> m) -> t a -> m | |
foldMap f = foldr (append . f) empty | |
-- | Right-associative fold of a structure | |
foldr :: (a -> b -> b) -> b -> t a -> b | |
foldr f z t = appEndo (foldMap (Endo . f) t) z | |
-- | Left-associative fold of a structure | |
foldl :: (b -> a -> b) -> b -> t a -> b | |
foldl f z t = appEndo (getMonoidDual (foldMap (MonoidDual . Endo . flip f) t)) z | |
-- | Left-associative fold of a structure with strict application | |
foldl' :: (b -> a -> b) -> b -> t a -> b | |
foldl' f z0 xs = foldr f' id xs z0 where | |
f' x k z = k $! f z x | |
-- | If you can fold it, you can filter it and map it | |
filter :: (Monoid (t a), Applicative t) => (a -> Bool) -> t a -> t a | |
filter p = foldMap (\a -> if p a then pure a else empty) | |
-- | Monadic left-associative fold over the elements of a structure | |
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b | |
foldlM f z0 xs = foldr f' return xs z0 | |
where f' x k z = f z x >>= k | |
{- | | |
- Bifoldable | |
- | |
- A foldable structure with two varieties of elements. | |
- Basically, sums and products. | |
-} | |
class Bifoldable p where | |
bifold :: Monoid m => p m m -> m | |
bifold = bifoldMap id id | |
{-# INLINE bifold #-} | |
bifoldMap :: Monoid m => (a -> m) -> (b -> m) -> p a b -> m | |
bifoldMap f g = bifoldr (append . f) (append . g) empty | |
bifoldr :: (a -> c -> c) -> (b -> c -> c) -> c -> p a b -> c | |
bifoldr f g z t = appEndo (bifoldMap (Endo . f) (Endo . g) t) z | |
instance Bifoldable (,) where | |
bifoldMap f g ~(a, b) = f a `append` g b | |
{-# INLINE bifoldMap #-} | |
instance Bifoldable Const where | |
bifoldMap f _ (Const a) = f a | |
{-# INLINE bifoldMap #-} | |
{- | | |
- Optional types | |
- Signifies uncertainty: a function which returns an Opt value may return the | |
- expected result, or none at all. | |
-} | |
newtype Opt a = Opt { maybe :: forall r. (a -> r) -> r -> r } | |
just :: a -> Opt a | |
just x = Opt $ \s n -> s x | |
none :: Opt a | |
none = Opt $ \s n -> n | |
instance Functor Opt where | |
fmap f o = maybe o (\x -> just (f x)) none | |
instance Applicative Opt where | |
pure = return | |
(<*>) = ap | |
instance Monad Opt where | |
return = just | |
o >>= f = maybe o (\x -> f x) none | |
fail _ = none | |
instance Alternative Opt where | |
zilch = none | |
o <|> r = maybe o (\j -> just j) r | |
instance Foldable Opt where | |
foldr f z o = maybe o (\x -> f x z) z | |
instance Monoid a => Monoid (Opt a) where | |
empty = none | |
append x y = maybe x (\x' -> | |
maybe y (\y' -> just (x' `append` y')) | |
x | |
) y | |
instance Show a => Show (Opt a) where | |
show o = maybe o (\x -> (P.++) "Value: " (show x)) "(none)" | |
{- | | |
- Either! | |
- | |
- A coproduct of two different types. Similar to `Opt` except both cases store | |
- values, which may be of different types. | |
-} | |
newtype Either a b = Either { either :: forall r. (a -> r) -> (b -> r) -> r } | |
left :: a -> Either a b | |
left x = Either $ \l r -> l x | |
{-# INLINE left #-} | |
right :: b -> Either a b | |
right y = Either $ \l r -> r y | |
{-# INLINE right #-} | |
instance Functor (Either a) where | |
fmap f e = either e (\x -> left x) (\y -> right (f y)) | |
instance Monad (Either a) where | |
return = right | |
e >>= k = either e left (\y -> k y) | |
instance Applicative (Either a) where | |
pure = return | |
(<*>) = ap | |
-- | This class basically exists to overload function names | |
class Sequence s where | |
repeat :: a -> s a | |
zipWith :: (a -> b -> c) -> s a -> s b -> s c | |
unfoldr :: (b -> (a, b)) -> b -> s a | |
{- | | |
- Fusable lists | |
- | |
- A Mendler encoding of a list. foldr is the catamorphism, accepting as | |
- arguments two possible continuations: one for receiving the next value in a | |
- list, and one for halting. | |
- | |
- Expresses the idea of recursion and iteration. | |
-} | |
newtype List a = List { listFoldr :: forall r. (a -> r -> r) -> r -> r } | |
-- | Maps a function over every element in a list. | |
map :: (a -> b) -> List a -> List b | |
map f xs = listFoldr xs (\y ys -> cons (f y) ys) nil | |
instance Functor List where | |
fmap = map | |
instance Applicative List where | |
pure = return | |
(<*>) = ap | |
instance Monad List where | |
return x = cons x nil | |
xs >>= f = concat (map f xs) | |
instance Alternative List where | |
zilch = nil | |
(<|>) = (++) | |
instance Monoid (List a) where | |
empty = nil | |
append x y = x ++ y | |
instance Foldable List where | |
foldr c n xs = listFoldr xs c n | |
filter = listFilter -- probably more efficient this way | |
instance Foldable [] where | |
foldr = P.foldr | |
filter = P.filter | |
-- | Construct a new list by prepending a value to an existing list | |
cons :: a -> List a -> List a | |
cons x xs = List $ \c e -> c x $ listFoldr xs c e | |
-- | Construct a new, empty list | |
nil :: List a | |
nil = List $ \c e -> e | |
-- | Computes the fixed point of a function. See `diverge` | |
fix :: (a -> a) -> a | |
fix f = f (fix f) | |
-- | Computes the fixed point of the identity function, resulting in a nonsense | |
-- value which can represent any type. See `car`. | |
diverge :: a | |
diverge = fix id | |
-- | Splits a list into its head and tail. Handles the empty case. | |
split :: List a -> (Opt a, List a) | |
split xs = listFoldr xs f (none, nil) where | |
f y ys = (just y, List (\c e -> | |
maybe (fst ys) (\x -> c x (listFoldr (snd ys) c e)) | |
e)) | |
-- | Name is a lisp term. `diverge` is used because `maybe` requires a third | |
-- argument, but in a non-empty list we will never actually need the value. | |
car :: List a -> a | |
car xs = maybe (fst $ split xs) id diverge | |
-- | Name is a lisp term. Grabs the head of the list, or none. | |
car' :: List a -> Opt a | |
car' xs = fst . split $ xs | |
-- | Name is a lisp term. Grabs the tail of a list. | |
cdr :: List a -> List a | |
cdr = snd . split | |
-- | Constructs a new list from an old one, omitting elements which do not | |
-- satisfy a predicate. List-specific version | |
listFilter :: (a -> Bool) -> List a -> List a | |
listFilter pred xs = listFoldr xs f nil where | |
f y ys = List $ \c e -> if (pred y) | |
then c y (listFoldr ys c e) | |
else listFoldr ys c e | |
-- | Appends two lists | |
(++) :: List a -> List a -> List a | |
xs ++ ys = listFoldr xs cons ys | |
-- | Zip the elements of two lists into a list of pairs | |
zip :: List a -> List b -> List (a, b) | |
zip as bs = | |
let (ha, ta) = split as | |
(hb, tb) = split bs | |
in maybe ha (\a -> | |
maybe hb (\b -> cons (a, b) (zip ta tb)) nil) | |
nil | |
-- | Generalizes 'zip' by zipping with the function given | |
listZipWith :: (a -> b -> c) -> List a -> List b -> List c | |
listZipWith f as bs = | |
let (mha, ta) = split as | |
(mhb, tb) = split bs | |
in maybe mha (\ha -> | |
maybe mhb (\hb -> cons (f ha hb) | |
(listZipWith f ta tb)) diverge | |
) diverge | |
-- | Flattens a list of lists. | |
concat :: List (List a) -> List a | |
concat xs = listFoldr xs (++) nil | |
-- | Splits a list of tuples into a tuple of lists. | |
unzip :: List (a, b) -> ((List a), (List b)) | |
unzip xs = listFoldr xs (\(a,b) (as,bs) -> (cons a as, cons b bs)) (nil, nil) | |
-- | Given a successor function and a seed value, computes an infinite list. | |
iterate :: (a -> a) -> a -> List a | |
iterate f x = cons x $ iterate f (f x) | |
-- | Length of a list | |
length :: List a -> Int | |
length xs = listFoldr xs (\_ -> (+1)) 0 | |
-- | Take the first n elements of a list | |
take :: Int -> List a -> List a | |
take = fix take' where | |
take' t n xs = listFoldr xs (\y ys -> | |
if n == 0 then nil else cons y (t (n-1) ys)) nil | |
-- | Repeat a value in an infinite list | |
listRepeat :: a -> List a | |
listRepeat x = xs where xs = cons x xs | |
-- | Replicate a value just finite number of times in a list | |
replicate :: Int -> a -> List a | |
replicate n x = take n (repeat x) | |
-- | Construct a singleton list | |
single :: a -> List a | |
single = flip cons nil | |
-- | Reverse a list | |
reverse :: Foldable t => t a -> List a | |
reverse = foldl (flip cons) nil | |
-- | Join lines, after appending a terminating newline to each | |
unlines :: List String -> String | |
unlines = foldMap (P.++ "\n") | |
-- | Because we are doing this in Haskell, to represent things we must use | |
-- `String`, which implicitly means conversion to `[]`, to show our list. | |
toPrimList :: List a -> [a] | |
toPrimList xs = listFoldr xs (\y ys -> y : ys) [] | |
instance Show a => Show (List a) where | |
show = show . toPrimList where | |
instance Sequence List where | |
repeat = listRepeat | |
zipWith = listZipWith | |
unfoldr f c = | |
let (x, d) = f c | |
in cons x (unfoldr f d) | |
{- | |
- The free monad and free monad transformer | |
- | |
- For any Functor type f, Mu gives a monad instance for f "for free." A monad | |
- may be thought of either as a terminal (or "unit" value) or a wrapped | |
- continuing computation. | |
- | |
- A monad transformer is a monad which can be layered on top of another | |
- existing monad, resulting in a new custom composition of the two. Here the | |
- canonical free monad, Mu, is defined in terms of the free monad transformer | |
- and Box, the identity functor. | |
-} | |
-- | All free monad implementations will become instances of this class. See | |
-- `improve`. | |
class (Functor f, Monad m) => FreeMonad f m | m -> f where | |
wrap :: f (m a) -> m a | |
liftF :: (FreeMonad f m) => f a -> m a | |
liftF = wrap . fmap return | |
-- | The base functor of a free monad | |
data MuF f a b = Pure a | Wrap (f b) | |
deriving (Eq, Ord, Show, Read) | |
instance Functor f => Functor (MuF f a) where | |
fmap _ (Pure a) = Pure a | |
fmap f (Wrap as) = Wrap (fmap f as) | |
{-# INLINE fmap #-} | |
instance (Functor f, Monad (MuF f a)) => Applicative (MuF f a) where | |
pure = return | |
(<*>) = ap | |
instance Foldable f => Foldable (MuF f a) where | |
foldMap f (Wrap as) = foldMap f as | |
foldMap _ _ = empty | |
{-# INLINE foldMap #-} | |
-- | The free monad transformer | |
newtype MuT f m a = MuT { runMuT :: m (MuF f a (MuT f m a)) } | |
-- | Finally, our first definition of a full free monad. | |
type Mu f = MuT f Box | |
runMu :: Mu f a -> MuF f a (Mu f a) | |
runMu = unbox . runMuT | |
free :: MuF f a (Mu f a) -> Mu f a | |
free = MuT . Box | |
{-# INLINE free #-} | |
instance (Functor f, Monad m) => Functor (MuT f m) where | |
fmap f (MuT m) = MuT (liftM f' m) where | |
f' (Pure a) = Pure (f a) | |
f' (Wrap as) = Wrap (fmap (fmap f) as) | |
instance (Functor f, Monad m) => Applicative (MuT f m) where | |
pure a = MuT (return (Pure a)) | |
{-# INLINE pure #-} | |
(<*>) = ap | |
instance (Functor f, Monad m) => Monad (MuT f m) where | |
return a = MuT (return (Pure a)) | |
{-# INLINE return #-} | |
MuT m >>= f = MuT $ m >>= \v -> case v of | |
Pure a -> runMuT (f a) | |
Wrap w -> return (Wrap (fmap (>>= f) w)) | |
{- | |
- Pedagogical aside: the monad bind operation | |
- | |
- The Monad typeclass calls this function `>>=` for reasons which will be | |
- illuminated shortly. | |
- | |
- Its first argument is a monadic value of just type `a`, and its second | |
- argument is a function `a -> m b`, where `m b` is a monadic value of type | |
- `b`. If you consider the second argument to be an expression, then `>>=` | |
- uses anonymous functions to bind the first argument in the expression. | |
- | |
- Without any syntax sugar, monadic computations look like this: | |
- | |
- mf = getX >>= \x -> | |
- getY >>= \y -> | |
- foo x y >>= \z -> | |
- dojustthingWith z >> | |
- return z | |
- | |
- Formatting this way is helpfully suggestive, I hope. It's not a stretch to | |
- see how this is transformed from do-notation: | |
- | |
- mf = do | |
- x <- getX | |
- y <- getY | |
- z <- foo x y | |
- dojustthingWith z | |
- return z | |
- | |
- There is another function, called `>>` or *next* which evaluates a monadic | |
- function and disregards the result. Given a working implementation of `>>=` | |
- it is written for you. | |
-} | |
-- | Our monad transformer is, in fact, a monad transformer. `liftM` is defined | |
-- below along with other monad utilities. | |
instance MonadTrans (MuT f) where | |
lift = MuT . liftM Pure | |
{-# INLINE lift #-} | |
instance (Functor f, Monad m) => FreeMonad f (MuT f m) where | |
wrap = MuT . return . Wrap | |
{-# INLINE wrap #-} | |
{- | | |
- Mendler-encoded monad transformer | |
- | |
- The above recursive definition of a monad is very useful and easy to work | |
- with. However, it requires building up a structure which will simply be torn | |
- down when evaluated. Most times, this is totally fine. Sometimes, though, | |
- this leads to quadratic space usage. | |
- | |
- Instead of building up a structure, an alternate way to think of a monad is | |
- a function which accepts two continuation functions, one for each | |
- constructor above. Depending on the value of the monad, it selects either | |
- continuation. | |
-} | |
newtype MT f m a = MT { runMT :: forall r. (a -> m r) -> (f (m r) -> m r) -> m r } | |
instance Functor (MT f m) where | |
fmap f (MT k) = MT $ \a fr -> k (a . f) fr | |
instance Applicative (MT f m) where | |
pure a = MT $ \k _ -> k a | |
MT fk <*> MT ak = MT $ \b fr -> ak (\d -> fk (\e -> b (e d)) fr) fr | |
instance Monad (MT f m) where | |
return = pure | |
MT fk >>= f = MT $ \b fr -> fk (\d -> runMT (f d) b fr) fr | |
instance Functor f => FreeMonad f (MT f m) where | |
wrap f = MT $ \u w -> w (fmap (\(MT m) -> m u w) f) | |
type M f = MT f Box | |
runM :: Functor f | |
=> M f a | |
-> (forall r. (a -> r) -> (f r -> r) -> r) | |
runM (MT m) = \u w -> unbox $ m (return . u) (return . w . fmap unbox) | |
-- | Generate a Mendler-encoded free monad from a `Mu` | |
toMT :: (Monad m, Functor f) => MuT f m a -> MT f m a | |
toMT (MuT f) = MT $ \ka kfr -> do | |
muf <- f | |
case muf of | |
Pure a -> ka a | |
Wrap fb -> kfr $ fmap (($ kfr) . ($ ka) . runMT . toMT) fb | |
toM :: (Functor f) => Mu f a -> M f a | |
toM = toMT | |
{-# INLINE toM #-} | |
-- | fromM can convert from M to any other FreeMonad instance. | |
fromM :: (Functor f, FreeMonad f m) => M f a -> m a | |
fromM m = runM m return wrap | |
{-# INLINE fromM #-} | |
{- | | |
- improve uses fromM to constrain the argument type as M, but then casts the | |
- resulting value as a Mu. Thus, generic FreeMonad values may be wrapped in | |
- improve and automatically see asymptotic improvements in performance. | |
-} | |
improve :: Functor f => (forall m. FreeMonad f m => m a) -> Mu f a | |
improve m = fromM m | |
{-# INLINE improve #-} | |
{- | |
- Simple monad utilities | |
- | |
- These are generic to any monad constructed using either free monad | |
- machinery. | |
-} | |
when :: (FreeMonad f m) => Bool -> m () -> m () | |
when p s = if p then s else return () | |
-- | TODO: Implement Traversable so this isn't List-specific | |
sequence :: (Foldable t, Monad m) => t (m a) -> m (List a) | |
sequence ms = foldr k (return nil) ms where | |
k m m' = do { x <- m; xs <- m'; return (cons x xs) } | |
{-# INLINE sequence #-} | |
-- | TODO: Implement Traversable so this isn't List-specific | |
mapM :: Monad m => (a -> m b) -> List a -> m (List b) | |
mapM f as = sequence (fmap f as) | |
{-# INLINE mapM #-} | |
sequence_ :: (Foldable t, Monad m) => t (m a) -> m () | |
sequence_ ms = foldr (>>) (return ()) ms | |
{-# INLINE sequence_ #-} | |
mapM_ :: (Foldable f, Functor f, Monad m) => (a -> m b) -> f a -> m () | |
mapM_ f as = sequence_ (fmap f as) | |
{-# INLINE mapM_ #-} | |
forever :: Monad m => m a -> m b | |
forever x = let x' = (>>) x x' in x' | |
{-# INLINE forever #-} | |
replicateM_ :: Monad m => Int -> m a -> m () | |
replicateM_ n x = sequence_ (replicate n x) | |
mapAndUnzipM f xs = sequence (map f xs) >>= return . unzip | |
liftM :: (Monad m) => (a1 -> r) -> m a1 -> m r | |
liftM f m1 = do { x1 <- m1; return (f x1) } | |
liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r | |
liftM2 f m1 m2 = do { x1 <- m1; x2 <- m2; return (f x1 x2) } | |
ap :: (Monad m) => m (a -> b) -> m a -> m b | |
ap = liftM2 id | |
unless :: (Monad m) => Bool -> m () -> m () | |
unless p s = if p then return () else s | |
(>=>) :: Monad m => (t -> m a) -> (a -> m b) -> t -> m b | |
f >=> g = \x -> f x >>= g | |
join :: (Monad m) => m (m a) -> m a | |
join x = x >>= id | |
{- | |
- Utility monads | |
- | |
- There are a few handy monad transformers one might want to stack together to | |
- create others. While psilo will, in all likelihood, come with some | |
- combination of the following pre-fab, it's important to see how they work | |
- independently for verification purposes. | |
-} | |
-- | The StateT monad transformer and the State monad derived from it | |
newtype StateT s m a = StateT { runStateT :: s -> m (a, s) } deriving Functor | |
type State s = StateT s Box | |
runState :: State s a | |
-> s | |
-> (a, s) | |
runState m = unbox . runStateT m | |
evalState = flip runState | |
evalStateT = flip runStateT | |
instance (Functor m, Monad m) => Applicative (StateT s m) where | |
pure = return | |
(<*>) = ap | |
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' | |
fail str = StateT $ \_ -> fail str | |
instance MonadTrans (StateT s) where | |
lift m = StateT $ \s -> do | |
a <- m | |
return (a, s) | |
-- | The requirements to be a State monad | |
class Monad m => MonadState s m | m -> s where | |
get :: m s | |
put :: s -> m () | |
-- | State now satisfies its own requirements | |
instance Monad m => MonadState s (StateT s m) where | |
get = StateT $ \s -> return (s, s) | |
put s = StateT $ \_ -> return ((), s) | |
instance (Functor f, FreeMonad f m) => FreeMonad f (StateT s m) where | |
wrap fm = StateT $ \s -> wrap $ flip runStateT s <$> fm | |
{- | | |
- The Continuation Monad & Transformer | |
- | |
- Provides the ability to suspend a computation to be continued elsewhere or | |
- at a later time. Continuations are sufficient to express any kind of control | |
- flow mechanism. | |
-} | |
newtype ContT r m a = ContT { runContT :: (a -> m r) -> m r } deriving (Functor) | |
evalContT :: (Monad m) => ContT r m r -> m r | |
evalContT m = runContT m return | |
withContT :: ((b -> m r) -> (a -> m r)) -> ContT r m a -> ContT r m b | |
withContT f m = ContT $ runContT m . f | |
instance Applicative (ContT r m) where | |
pure x = ContT ($ x) | |
f <*> v = ContT $ \c -> runContT f $ \g -> runContT v (c . g) | |
instance Monad (ContT r m) where | |
return x = ContT ($ x) | |
m >>= k = ContT $ \c -> runContT m (\x -> runContT (k x) c) | |
instance MonadTrans (ContT r) where | |
lift m = ContT (m >>=) | |
callCC' :: ((a -> ContT r m b) -> ContT r m a) -> ContT r m a | |
callCC' f = ContT $ \c -> runContT (f (\x -> ContT $ \_ -> c x)) c | |
resetT :: (Monad m) => ContT r m r -> ContT r' m r | |
resetT = lift . evalContT | |
shiftT :: (Monad m) => ((a -> m r) -> ContT r m r) -> ContT r m a | |
shiftT f = ContT (evalContT . f) | |
type Cont r = ContT r Box | |
cont :: ((a -> r) -> r) -> Cont r a | |
cont f = ContT (\c -> Box (f (unbox . c))) | |
runCont :: Cont r a -> (a -> r) -> r | |
runCont m k = unbox (runContT m (Box . k)) | |
evalCont :: Cont r r -> r | |
evalCont m = unbox (evalContT m) | |
withCont :: ((b -> r) -> (a -> r)) -> Cont r a -> Cont r b | |
withCont f = withContT ((Box .) . f . (unbox .)) | |
reset :: Cont r r -> Cont r' r | |
reset = resetT | |
shift :: ((a -> r) -> Cont r r) -> Cont r a | |
shift f = shiftT (f . (unbox .)) | |
class Monad m => MonadCont m where | |
callCC :: ((a -> m b) -> m a) -> m a | |
instance MonadCont (ContT r m) where | |
callCC = callCC' | |
-- | The ReaderT monad transformer models a stack-like binding environment | |
newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a } deriving Functor | |
type Reader r = ReaderT r Box | |
reader :: (Monad m) => (r -> a) -> ReaderT r m a | |
reader f = ReaderT (return . f) | |
runReader :: Reader r a | |
-> r | |
-> a | |
runReader m = unbox . runReaderT m | |
instance (Applicative m) => Applicative (ReaderT r m) where | |
pure = liftReaderT . pure | |
f <*> v = ReaderT $ \ r -> runReaderT f r <*> runReaderT v r | |
instance (Monad m) => Monad (ReaderT r m) where | |
return = lift . return | |
m >>= k = ReaderT $ \r -> do | |
a <- runReaderT m r | |
runReaderT (k a) r | |
instance MonadTrans (ReaderT r) where | |
lift = liftReaderT | |
liftReaderT :: m a -> ReaderT r m a | |
liftReaderT m = ReaderT (const m) | |
class (Monad m) => MonadReader r m | m -> r where | |
ask :: m r | |
local :: (r -> r) -- ^ The function to modify the environment | |
-> m a -- ^ Reader to run in the modified environment | |
-> m a | |
withReaderT :: (r' -> r) -> ReaderT r m a -> ReaderT r' m a | |
withReaderT f m = ReaderT $ runReaderT m . f | |
instance (Monad m) => MonadReader r (ReaderT r m) where | |
ask = ReaderT return | |
local = withReaderT | |
instance (Functor f, FreeMonad f m) => FreeMonad f (ReaderT r m) where | |
wrap fm = ReaderT $ \e -> wrap $ flip runReaderT e <$> fm | |
class (Monad m) => MonadIO m where | |
liftIO :: IO a -> m a | |
instance MonadIO IO where | |
liftIO = id | |
instance (MonadIO m) => MonadIO (StateT s m) where | |
liftIO = lift . liftIO | |
instance (MonadIO m) => MonadIO (ContT r m) where | |
liftIO = lift . liftIO | |
instance (MonadIO m) => MonadIO (ReaderT s m) where | |
liftIO = lift . liftIO | |
{- | WriterT Monad Transformer -} | |
newtype WriterT w m a = WriterT { runWriterT :: m (a, w) } | |
mapWriterT :: (m (a, w) -> n (b, w')) -> WriterT w m a -> WriterT w' n b | |
mapWriterT f m = WriterT $ f (runWriterT m) | |
instance (Functor m) => Functor (WriterT w m) where | |
fmap f = mapWriterT $ fmap $ \ (a, w) -> (f a, w) | |
instance (Foldable f) => Foldable (WriterT w f) where | |
foldMap f = foldMap (f . fst) . runWriterT | |
instance (Monoid w, Applicative m) => Applicative (WriterT w m) where | |
pure a = WriterT $ pure (a, empty) | |
f <*> v = WriterT $ liftA2 k (runWriterT f) (runWriterT v) | |
where k (a, w) (b, w') = (a b, w `append` w') | |
instance (Monoid w, Alternative m) => Alternative (WriterT w m) where | |
zilch = WriterT zilch | |
m <|> n = WriterT $ runWriterT m <|> runWriterT n | |
instance (Monoid w, Monad m) => Monad (WriterT w m) where | |
return a = writer (a, empty) | |
m >>= k = WriterT $ do | |
(a, w) <- runWriterT m | |
(b, w') <- runWriterT (k a) | |
return (b, w `append` w') | |
fail msg = WriterT $ fail msg | |
instance (Monoid w, MonadPlus m) => MonadPlus (WriterT w m) where | |
mzero = WriterT mzero | |
m `mplus` n = WriterT $ runWriterT m `mplus` runWriterT n | |
instance (Monoid w) => MonadTrans (WriterT w) where | |
lift m = WriterT $ do | |
a <- m | |
return (a, empty) | |
instance (Monoid w, MonadIO m) => MonadIO (WriterT w m) where | |
liftIO = lift . liftIO | |
type Writer w = WriterT w Box | |
-- | Construct a writer computation from a (result, output) pair. | |
-- (The inverse of 'runWriter'.) | |
writer :: (Monad m) => (a, w) -> WriterT w m a | |
writer = WriterT . return | |
-- | Unwrap a writer computation as a (result, output) pair. | |
-- (The inverse of 'writer'.) | |
runWriter :: Writer w a -> (a, w) | |
runWriter = unbox . runWriterT | |
-- | Extract the output from a writer computation. | |
-- | |
-- * @'execWriter' m = 'snd' ('runWriter' m)@ | |
execWriter :: Writer w a -> w | |
execWriter m = snd (runWriter m) | |
-- | Map both the return value and output of a computation using | |
-- the given function. | |
-- | |
-- * @'runWriter' ('mapWriter' f m) = f ('runWriter' m)@ | |
mapWriter :: ((a, w) -> (b, w')) -> Writer w a -> Writer w' b | |
mapWriter f = mapWriterT (Box . f . unbox) | |
{- Already provided by GHC base libraries | |
instance Monad ((->) r) where | |
return = const | |
f >>= k = \ r -> k (f r) r | |
-} | |
instance (Monoid w) => Monad ((,) w) where | |
return x = (empty, x) | |
(w, x) >>= f = let (w', y) = f x in (w <> w', y) | |
{- | | |
- MonadPlus | |
- | |
- Monads that support choice and failure | |
-} | |
class Monad m => MonadPlus m where | |
mzero :: m a | |
mplus :: m a -> m a -> m a | |
instance MonadPlus List where | |
mzero = nil | |
mplus = (++) | |
instance MonadPlus Opt where | |
mzero = none | |
o `mplus` ys = maybe o (\xs -> just xs) ys | |
{- | | |
- Comonads | |
- | |
- Represents a computation in context. The dual concept to monads. | |
- | |
- Where monads have the following functions defined | |
- | |
- return :: a -> m a | |
- (>>=) :: m a -> (a -> m b) -> m b | |
- | |
- a comonad has | |
- | |
- extract :: w a -> a | |
- extend :: (w a -> b) -> w a -> w b | |
- | |
- A comonad is a computation wherein an operation is performed simultaneously | |
- in all possible future states. | |
- | |
- Applications include stream processing, reactive programming, and grid | |
- computing. | |
- | |
-} | |
class Functor w => Comonad w where | |
extract :: w a -> a | |
duplicate :: w a -> w (w a) | |
duplicate = extend id | |
extend :: (w a -> b) -> w a -> w b | |
extend f = fmap f . duplicate | |
class (Functor f, Comonad w) => CofreeComonad f w | w -> f where | |
unwrap :: w a -> f (w a) | |
instance Comonad ((,) e) where | |
extract = snd | |
duplicate p = (fst p, p) | |
instance Comonad (Pair e) where | |
extract = snd' | |
duplicate p = pair (p .! _fst) p | |
instance (Monoid m) => Comonad ((->) m) where | |
extract f = f empty | |
extend f wa = \x -> f $ \y -> (wa (append x y)) | |
-- duplicate wa x = wa . append x | |
-- | The underlying functor of a comonad, here called NuF. | |
data NuF f a b = a :< f b deriving (Eq, Ord, Show, Read) | |
headF :: NuF f a b -> a | |
headF (a :< _) = a | |
tailF :: NuF f a b -> f b | |
tailF (_ :< as) = as | |
instance Functor f => Functor (NuF f a) where | |
fmap f (a :< as) = a :< fmap f as | |
instance Foldable f => Foldable (NuF f a) where | |
foldMap f (_ :< as) = foldMap f as | |
instance Functor f => Bifunctor (NuF f) where | |
bimap f g (a :< as) = f a :< fmap g as | |
instance Foldable f => Bifoldable (NuF f) where | |
bifoldMap f g (a :< as) = f a `append` foldMap g as | |
-- | Comonad transformer: analogous to monad transformer | |
newtype NuT f w a = NuT { | |
runNuT :: w (NuF f a (NuT f w a)) } | |
-- | The simplified cofree comonad, Nu | |
type Nu f = NuT f Box | |
cofree :: NuF f a (Nu f a) -> Nu f a | |
cofree = NuT . Box | |
{-# INLINE cofree #-} | |
runNu :: Nu f a -> NuF f a (Nu f a) | |
runNu = unbox . runNuT | |
{-# INLINE runNu #-} | |
instance (Functor f, Functor w) => Functor (NuT f w) where | |
fmap f = NuT . fmap (bimap f (fmap f)) . runNuT | |
instance (Foldable f, Foldable w) => Foldable (NuT f w) where | |
foldMap f = foldMap (bifoldMap f (foldMap f)) . runNuT | |
instance (Functor f, Comonad w) => Comonad (NuT f w) where | |
extract = headF . extract . runNuT | |
extend f = NuT . extend (\w -> f (NuT w) :< (extend f | |
<$> tailF (extract w))) . runNuT | |
instance (Functor f, Comonad w) => CofreeComonad f (NuT f w) where | |
unwrap = tailF . extract . runNuT | |
-- | A comonad based on a monoidal functor - such as Alternative - is a monad! | |
instance (Alternative f, Monad w) => Monad (NuT f w) where | |
return = NuT . return . (:< zilch) | |
{-# INLINE return #-} | |
NuT cx >>= f = NuT $ do | |
a :< m <- cx | |
b :< n <- runNuT $ f a | |
return $ b :< (n <|> fmap (>>= f) m) | |
-- | Applicative definition, for completeness | |
instance (Alternative f, Applicative w) => Applicative (NuT f w) where | |
pure = NuT . pure . (:< zilch) | |
{-# INLINE pure #-} | |
wf <*> wa = NuT $ go <$> runNuT wf <*> runNuT wa where | |
go (f :< t) a = case bimap f (fmap f) a of | |
b :< n -> b :< (n <|> fmap (<*> wa) t) | |
{-# INLINE (<*>) #-} | |
instance Alternative f => MonadTrans (NuT f) where | |
lift = NuT . liftM (:< zilch) | |
-- | Comonad transformer class | |
class ComonadTrans t where | |
lower :: Comonad w => t w a -> w a | |
instance Functor f => ComonadTrans (NuT f) where | |
lower = fmap headF . runNuT | |
class Comonad w => ComonadApply w where | |
(<@>) :: w (a -> b) -> w a -> w b | |
(@>) :: w a -> w b -> w b | |
(<@) :: w a -> w b -> w a | |
instance ComonadApply Box where | |
(<@>) = (<*>) | |
(<@) = (<*) | |
(@>) = (*>) | |
{- | |
- Comonad utilities | |
-} | |
coiter :: Functor f => (a -> f a) -> a -> Nu f a | |
coiter psi a = cofree $ a :< (coiter psi <$> psi a) | |
unfold :: Functor f => (b -> (a, f b)) -> b -> Nu f a | |
unfold f c = cofree $ case f c of | |
(x, d) -> x :< fmap (unfold f) d | |
coiterT :: (Functor f, Comonad w) => (w a -> f (w a)) -> w a -> NuT f w a | |
coiterT psi = NuT . extend (\w -> extract w :< fmap (coiterT psi) (psi w)) | |
(=>>) :: Comonad w => w a -> (w a -> b) -> w b | |
a =>> cb = extend cb a | |
wfix :: Comonad w => w (w a -> a) -> a | |
wfix w = extract w (extend wfix w) | |
cfix :: Comonad w => (w a -> a) -> w a | |
cfix f = fix (extend f) | |
{-# INLINE cfix #-} | |
(=>=) :: Comonad w => (w a -> b) -> (w b -> c) -> w a -> c | |
f =>= g = g . extend f | |
{-# INLINE (=>=) #-} | |
{- | |
- Useful comonads | |
-} | |
{- | | |
- Store is the dual of State | |
-} | |
data StoreT s w a = StoreT (w (s -> a)) s deriving (Functor) | |
type Store s = StoreT s Box | |
store :: (s -> a) -> s -> Store s a | |
store f s = StoreT (Box f) s | |
runStore :: Store s a -> (s -> a, s) | |
runStore (StoreT (Box f) s) = (f, s) | |
instance Comonad w => Comonad (StoreT s w) where | |
duplicate (StoreT wf s) = StoreT (extend StoreT wf) s | |
extend f (StoreT wf s) = StoreT (extend (\wf' s' -> f (StoreT wf' s')) wf) s | |
extract (StoreT wf s) = extract wf s | |
instance ComonadTrans (StoreT s) where | |
lower (StoreT f s) = fmap ($ s) f | |
class Comonad w => ComonadStore s w | w -> s where | |
pos :: w a -> s | |
peek :: s -> w a -> a | |
peeks :: (s -> s) -> w a -> a | |
seek :: s -> w a -> w a | |
seeks :: (s -> s) -> w a -> w a | |
instance Comonad w => ComonadStore s (StoreT s w) where | |
pos (StoreT _ s) = s | |
peek s (StoreT g _) = extract g s | |
peeks f (StoreT g s) = extract g (f s) | |
seek s (StoreT f _) = StoreT f s | |
seeks f (StoreT g s) = StoreT g (f s) | |
experiment :: (Comonad w, Functor f) => (s -> f s) -> StoreT s w a -> f a | |
experiment f (StoreT wf s) = extract wf <$> f s | |
{- | | |
- Env is the dual of Reader | |
-} | |
data EnvT e w a = EnvT e (w a) | |
type Env e = EnvT e Box | |
-- | Create an Env using an environment and a value | |
env :: e -> a -> Env e a | |
env e a = EnvT e (Box a) | |
runEnv :: Env e a -> (e, a) | |
runEnv (EnvT e (Box a)) = (e, a) | |
runEnvT :: EnvT e w a -> (e, w a) | |
runEnvT (EnvT e wa) = (e, wa) | |
instance Functor w => Functor (EnvT e w) where | |
fmap g (EnvT e wa) = EnvT e (fmap g wa) | |
instance Comonad w => Comonad (EnvT e w) where | |
duplicate (EnvT e wa) = EnvT e (extend (EnvT e) wa) | |
extract (EnvT _ wa) = extract wa | |
instance ComonadTrans (EnvT e) where | |
lower (EnvT _ wa) = wa | |
lowerEnvT :: EnvT e w a -> w a | |
lowerEnvT (EnvT _ wa) = wa | |
instance Foldable w => Foldable (EnvT e w) where | |
foldMap f (EnvT _ w) = foldMap f w | |
class Comonad w => ComonadEnv e w | w -> e where | |
query :: w a -> e | |
instance Comonad w => ComonadEnv e (EnvT e w) where | |
query (EnvT e _) = e | |
queries :: (e -> f) -> EnvT e w a -> f | |
queries f (EnvT e _) = f e | |
modify :: (e -> e') -> EnvT e w a -> EnvT e' w a | |
modify f (EnvT e wa) = EnvT (f e) wa | |
{- | |
- Example Env usage: searching a binary tree | |
- | |
- source: https://gist.github.com/ruicc/5435acba4be89aed7d6a | |
-} | |
data Bin a = Node a (Bin a) (Bin a) | Leaf a | |
bintree_ex :: Bin Int | |
bintree_ex = Node 5 (Leaf 3) (Node 8 (Leaf 7) (Leaf 10)) | |
searchTree :: Int -> Bin Int-> Opt Int | |
searchTree n t = let w = env t () in | |
extract $ | |
w =>> | |
query =>> \wbt -> case extract wbt of | |
Leaf a | |
| a == n -> just a | |
| otherwise -> none | |
Node a l r | |
| n == a -> just a | |
| n > a -> searchTree n r | |
| otherwise -> searchTree n l | |
test_search_binary_tree :: Opt Int | |
test_search_binary_tree = searchTree 8 bintree_ex | |
-- | Traced | |
newtype TracedT m w a = TracedT { runTracedT :: w (m -> a) } | |
type Traced m = TracedT m Box | |
traced :: (m -> a) -> Traced m a | |
traced f = TracedT (Box f) | |
runTraced :: Traced m a -> m -> a | |
runTraced (TracedT (Box f)) = f | |
instance Functor w => Functor (TracedT m w) where | |
fmap g = TracedT . fmap (g .) . runTracedT | |
instance (Comonad w, Monoid m) => Comonad (TracedT m w) where | |
extend f = TracedT . extend | |
(\wf m -> f (TracedT (fmap (. append m) wf))) . runTracedT | |
extract (TracedT wf) = extract wf empty | |
instance (Monoid m) => ComonadTrans (TracedT m) where | |
lower = fmap ($ empty) . runTracedT | |
class Comonad w => ComonadTraced m w | w -> m where | |
trace :: m -> w a -> a | |
instance (Comonad w, Monoid m) => ComonadTraced m (TracedT m w) where | |
trace m (TracedT wf) = extract wf m | |
listen :: Functor w => TracedT m w a -> TracedT m w (a, m) | |
listen = TracedT . fmap (\f m -> (f m, m)) . runTracedT | |
listens :: Functor w => (m -> b) -> TracedT m w a -> TracedT m w (a, b) | |
listens g = TracedT . fmap (\f m -> (f m, g m)) . runTracedT | |
censor :: Functor w => (m -> m) -> TracedT m w a -> TracedT m w a | |
censor g = TracedT . fmap (. g) . runTracedT | |
{- | | |
- Lenses | |
- | |
- A lens allows you to *focus* on an element nested inside just data structure | |
- for O(1) accesses and updates. The concept is very flexible and powerful, | |
- such that lenses may be mechanically generated for most any type. | |
- | |
- Lenses also give the ability to chain modifications to an object one after | |
- the other. | |
- | |
- One interesting application is in stream processors: receive an object from | |
- upstream and yield a modified version in one expression. | |
-} | |
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t | |
over :: Lens s t a b -> (a -> b) -> s -> t | |
over l f s = unbox (l (Box . f) s) | |
view :: Lens s t a b -> s -> a | |
view l s = getConst (l Const s) | |
(.!) :: s -> Lens s t a b -> a | |
s .! l = view l s | |
infixr 8 .! | |
(#) :: a -> (a -> b) -> b | |
x # y = y $ x | |
infixl 1 # | |
(.=) :: Lens s t a b -> b -> s -> t | |
l .= f = unbox . l (Box . (\_ -> f)) | |
infixr 4 .= | |
(.$) :: Lens s t a b -> (a -> b) -> s -> t | |
l .$ f = unbox . l (Box . f) | |
infixr 4 .$ | |
-- | Instances for types we've seen so far | |
hd :: Lens [a] [a] a a | |
hd f (a:as) = fmap go (f a) where | |
go a' = a': as | |
hd' :: Lens (List a) (List a) a a | |
hd' f xs = let (my, ys) = split xs | |
go a' = cons a' ys | |
in maybe my (\x -> fmap go (f x)) diverge | |
_1 :: Lens (a, b) (a', b) a a' | |
_1 f (a,b) = fmap (\x -> (x, b)) (f a) | |
_2 :: Lens (a, b) (a, b') b b' | |
_2 f (a,b) = fmap (\x -> (a, x)) (f b) | |
_fst :: Lens (Pair a b) (Pair a' b) a a' | |
_fst f p = unpair p (\a b -> fmap (\x -> (pair x b)) (f a)) | |
_snd :: Lens (Pair a b) (Pair a b') b b' | |
_snd f p = unpair p (\a b -> fmap (\x -> (pair a x)) (f b)) | |
stored f aStore = | |
let (ev, c) = runStore aStore | |
in fmap (\c' -> store ev c') (f c) | |
-- | Opens up a box | |
deref :: Lens (Box a) (Box b) a b | |
deref f (Box v) = fmap Box (f v) | |
{- | | |
- Task | |
- | |
- Task is the combination of two different ideas: sources and sinks. The free | |
- monad transformer of `((,) t)` for any value of type `t` allows a monadic | |
- computation to suspend itself and yield some intermediate value. | |
- | |
- type Source t = MuT ((,) t) | |
- | |
- Similarly, the free monad transformer of `((->) t)` for any value of type | |
- `t` allows a monadic computation to suspend and wait for more input. | |
- | |
- type Sink t = MuT ((->) t) | |
- | |
- A `yield` operator like in Python can then be defined as follows: | |
- | |
- yield :: Monad m => t -> Source t m () | |
- yield x = liftF (x, ()) | |
- | |
- And an `await` operator can be defined like so: | |
- | |
- await :: Monad m => Sink t m t | |
- await = liftF id | |
- | |
- Thus, a computation which can suspend execution to yield or demand | |
- intermediate results is a task. | |
- | |
- Combined into one type, they become a tool for composing sophisticated | |
- stream processing pipelines. | |
-} | |
data TaskF a b k | |
= Await (a -> k) | |
| Yield b k | |
deriving (Functor) | |
type Task a b = MuT (TaskF a b) | |
type Source b m r = Task () b m r | |
type Sink a m r = Task a () m r | |
type Result m r = Task () () m r | |
-- Task building blocks | |
yield :: FreeMonad (TaskF a b) m => b -> m () | |
yield x = liftF $ Yield x () | |
await :: FreeMonad (TaskF a b) m => m a | |
await = liftF $ Await id | |
liftT = lift . runMuT | |
cat :: FreeMonad (TaskF a a) m => m b | |
cat = forever $ await >>= yield | |
run = runMuT | |
-- Task composition functions | |
-- | Connect a task to a continuation yielding another task | |
(>-) :: Monad m | |
=> Task a b m r | |
-> (b -> Task b c m r) | |
-> Task a c m r | |
p >- f = liftT p >>= go where | |
go (Pure x) = return x | |
go (Wrap (Await f')) = wrap $ Await (\a -> (f' a) >- f) | |
go (Wrap (Yield v k)) = k >< f v | |
-- | Compose two tasks in a pull-based stream | |
(><) :: Monad m | |
=> Task a b m r | |
-> Task b c m r | |
-> Task a c m r | |
a >< b = liftT b >>= go where | |
go (Pure x) = return x | |
go (Wrap (Yield v k)) = wrap $ Yield v $ liftT k >>= go | |
go (Wrap (Await f)) = a >- f | |
infixl 3 >< | |
instance (Monad m, Monoid r) => Monoid (Task a b m r) where | |
empty = return empty | |
append p1 p2 = liftT p1 >>= go where | |
go (Wrap (Await f)) = wrap $ Await (\a -> liftT (f a) >>= go) | |
go (Wrap (Yield v k)) = wrap $ Yield v $ liftT k >>= go | |
go (Pure r) = fmap (append r) p2 | |
for :: Monad m | |
=> Task a b m r | |
-> (b -> Task a c m s) | |
-> Task a c m r | |
for src body = liftT src >>= go where | |
go (Wrap (Await f)) = wrap $ Await (\x -> liftT (f x) >>= go) | |
go (Wrap (Yield v k)) = do | |
body v | |
liftT k >>= go | |
go (Pure x) = return x | |
each :: (Monad m, Functor t, Foldable t) => t b -> Task a b m () | |
each = mapM_ yield | |
each' xs = (each xs >< taskmap just) >> yield none | |
next :: Monad m => Source a m r -> m (Either r (a, Source a m r)) | |
next src = runMuT src >>= go where | |
go (Wrap (Yield v k)) = return (right (v, k)) | |
go (Pure r) = return (left r) | |
tasktake :: Monad m => Int -> Task a a m () | |
tasktake n = replicateM_ n $ do | |
x <- await | |
yield x | |
-- | Strict left fold of a Source | |
reduce :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Source a m () -> m b | |
reduce step begin done p0 = runMuT p0 >>= \p' -> loop p' begin where | |
loop p x = case p of | |
Wrap (Yield v k) -> runMuT k >>= \k' -> loop k' $! step x v | |
Pure _ -> return (done x) | |
taskmap :: (Monad m) => (a -> b) -> Task a b m r | |
taskmap f = for cat $ \x -> yield (f x) | |
-- | Convert a Foldable (such as a Stream) into a Source | |
toSource :: (Foldable t, Functor t, Monad m) | |
=> t c | |
-> Task a c m () | |
toSource strm = for (each strm) yield | |
-- Examples; every iteratee / pipes library does this already | |
insult :: FreeMonad (TaskF String String) m => m b | |
insult = forever $ do | |
it <- await | |
yield $ it P.++ " sucks" | |
backpedal :: FreeMonad (TaskF String String) m => m b | |
backpedal = forever $ do | |
it <- await | |
yield $ it P.++ " (but not really)" | |
print = forever $ do | |
thing <- await | |
liftIO . putStrLn . show $ thing | |
prompt :: Source String IO () | |
prompt = forever $ do | |
liftIO $ putStr "> " | |
str <- liftIO getLine | |
yield str | |
pipeline_2 = runMuT $ (forever prompt) >< insult >< backpedal >< print | |
instance (MonadIO m) => MonadIO (Task a b m) where | |
liftIO = lift . liftIO | |
instance (MonadState s m) => MonadState s (Task a b m) where | |
get = lift get | |
put = lift . put | |
print' :: (Show a) => Task a a IO () | |
print' = forever $ do | |
it <- await | |
liftIO . putStrLn . show $ it | |
yield it | |
withState :: Monad m => b -> MuT f (StateT b m) a -> m b | |
withState s t = (evalStateT s (runMuT t)) >>= return . snd | |
(+>) = withState | |
infixl 4 +> | |
pipeline_3 = (each (cons 1 (cons 2 (cons 3 nil)))) >< print' # reduce (+) 0 id | |
instance Monad m => Show (Task a b m r) where | |
show _ = "<Task>" | |
-- Concurrency primitives for tasks | |
broadcast :: (Functor f, Monad m ) | |
=> Task a b m r | |
-> f ( Task b c m r ) | |
-> f ( Task a c m r ) | |
broadcast src tsks = fmap (\t -> src >< t) tsks | |
(*<) :: (Functor f, Monad m) | |
=> Task a b m r | |
-> f ( Task b c m r ) | |
-> f ( Task a c m r ) | |
(*<) = broadcast | |
infixr 4 *< | |
merge :: ( Functor t, Foldable t, Monad m ) | |
=> t (Task a c m s ) | |
-> Task a c m () | |
merge tasks = for (each tasks) $ \t -> for t yield | |
(>*) :: ( Functor t, Foldable t, Monad m ) | |
=> t ( Task a b m s ) | |
-> Task b c m () | |
-> Task a c m () | |
tasks >* k = (merge tasks) >< k | |
infixr 0 >* | |
{-| | |
Arithmetic parsing example | |
-} | |
data Arithmetic | |
= Value Int | |
| Add Arithmetic Arithmetic | |
| Mul Arithmetic Arithmetic | |
evalArith :: Arithmetic -> Int | |
evalArith (Value v) = v | |
evalArith (Add l r) = (evalArith l) + (evalArith r) | |
evalArith (Mul l r) = (evalArith l) * (evalArith r) | |
type Token = Opt String | |
tokenize :: Monad m => Task (Opt Char) Token m () | |
tokenize = loop "" where | |
loop acc = do | |
token <- await | |
maybe token | |
(\t -> case t of | |
' ' -> (yield (just acc)) >> loop "" | |
_ -> loop $ acc P.++ [t]) | |
(yield (just acc) >> yield none) | |
parseArith :: Monad m => Source Token m () -> m Int | |
parseArith = reduce step nil (evalArith . car) where | |
step stack token = maybe token | |
(\t -> case t of | |
"+" -> performOp (Add) stack | |
"*" -> performOp (Mul) stack | |
"" -> stack | |
_ -> cons (Value (read t)) stack) | |
stack | |
performOp op stack = | |
if length stack < 2 | |
then stack | |
else | |
let (m1, r1) = split stack | |
(m2, rst ) = split r1 | |
in maybe m1 | |
(\r -> maybe m2 | |
(\l -> cons (op l r) rst) | |
diverge) diverge | |
compute :: Monad m => String -> m Int | |
compute x = parseArith $ each' x >< tokenize | |
calculator :: IO () | |
calculator = forever $ do | |
putStr $ "> " | |
expr <- getLine | |
result <- compute expr | |
putStrLn . show $ result | |
-- | arbitrary data type | |
data Person = Person | |
{ _name :: String | |
, _age :: Int | |
} deriving (Show) | |
-- lenses | |
name f (Person n a) = fmap (\n' -> Person n' a) (f n) | |
age f (Person n a) = fmap (\a' -> Person n a') (f a) | |
g = Person "gatlin" 26 | |
w = Person "washington" 283 | |
people = cons g $ | |
cons w nil | |
birthday :: Monad m => Task Person Person m () | |
birthday = taskmap (# age .$ (+1)) | |
people_example = run $ each people | |
>< birthday | |
>< taskmap show | |
{- | | |
- List Zippers and Plane Zippers | |
-} | |
-- | Simple case: Löb's theorem applied to functors | |
loeb :: (Functor f) => f (f a -> a) -> f a | |
loeb x = fmap (\a -> a (loeb x)) x | |
test_loeb_1 = cons length (cons car nil) | |
-- loeb test_loeb_1 == [ 2 , 2 ] | |
test_loeb_2 = cons (const 0) $ | |
cons (\xs -> (car xs) + 5) $ | |
cons (const 7) $ | |
cons (\xs -> (car (cdr xs)) * 2) $ | |
cons (\xs -> (car xs) - 3) | |
nil | |
-- | A bidirectional stream with a focus | |
data Cursor a = Cursor | |
{ _viewL :: List a | |
, _focus :: a | |
, _viewR :: List a | |
} deriving (Functor, Show) | |
focus g (Cursor l f r) = fmap (\f' -> Cursor l f' r) (g f) | |
seed :: (c -> (a, c)) | |
-> (c -> a) | |
-> (c -> (a, c)) | |
-> c | |
-> Cursor a | |
seed prev center next = | |
Cursor <$> unfoldr prev <*> center <*> unfoldr next | |
iter :: (a -> a) | |
-> (a -> a) | |
-> a | |
-> Cursor a | |
iter prev next = | |
seed (dup . prev) id (dup . next) | |
where dup a = (a, a) | |
moveL :: Cursor a -> Cursor a | |
moveL (Cursor lxs c rxs) = | |
let lh = car lxs | |
lt = cdr lxs | |
in Cursor lt lh (cons c rxs) | |
moveR :: Cursor a -> Cursor a | |
moveR (Cursor lxs c rxs) = | |
let rh = car rxs | |
rt = cdr rxs | |
in Cursor (cons c lxs) rh rt | |
instance Comonad Cursor where | |
extract (Cursor _ c _) = c | |
duplicate = iter moveL moveR | |
-- | Löb's theorem but for comonads | |
evaluate :: (Comonad w) => w (w a -> a) -> w a | |
evaluate = extend wfix -- a very happy accident after playing type tetris | |
cursor_1 :: Cursor (Cursor Int -> Int) | |
cursor_1 = let n = const 0 in Cursor (repeat n) n (repeat n) | |
cursor_2 = cursor_1 # | |
moveL # | |
insert (\t -> 2 + (t # moveR # extract)) # | |
moveR | |
cursor_2_ev = evaluate cursor_2 | |
slice :: Int -> Cursor a -> List a | |
slice n (Cursor ls x rs) = (single x) ++ take n rs | |
-- cursor_2 # moveL # extract => 2 | |
data Plane a = Plane (Cursor (Cursor a)) | |
up :: Plane a -> Plane a | |
up (Plane p) = Plane (moveL p) | |
down :: Plane a -> Plane a | |
down (Plane p) = Plane (moveR p) | |
moveLeft :: Plane a -> Plane a | |
moveLeft (Plane p) = Plane (fmap moveL p) | |
moveRight :: Plane a -> Plane a | |
moveRight (Plane p) = Plane (fmap moveR p) | |
class Insertable i where | |
insert :: a -> i a -> i a | |
instance Insertable Cursor where | |
insert x (Cursor l _ r) = Cursor l x r | |
instance Insertable Plane where | |
insert x (Plane p) = | |
Plane $ insert newLine p where | |
newLine = insert x oldLine | |
oldLine = extract p | |
instance Functor Plane where | |
fmap f (Plane p) = Plane (fmap (fmap f) p) | |
horizontal :: Plane a -> Cursor (Plane a) | |
horizontal = iter moveLeft moveRight | |
vertical :: Plane a -> Cursor (Plane a) | |
vertical = iter up down | |
instance Comonad Plane where | |
extract (Plane p) = extract $ extract p | |
duplicate z = | |
Plane $ fmap horizontal $ vertical z | |
makePlane :: a -> List (List a) -> Plane a | |
makePlane def grid = Plane $ Cursor (repeat fz) fz rs where | |
rs = (map line grid) ++ repeat fz | |
dl = repeat def | |
fz = Cursor dl def dl | |
line l = Cursor dl def (l ++ dl) | |
sheet1 :: Plane (Plane Int -> Int) | |
sheet1 = makePlane (const 0) | |
(cons ( | |
cons (\c -> 15 + (c # moveLeft # extract)) ( | |
cons (\c -> 10 + (c # moveLeft # extract)) nil ) ) | |
(cons ( | |
cons (\c -> 2 * (c # up # extract)) nil ) | |
nil ) | |
) | |
neighborhood :: Int -> Plane a -> List (List a) | |
neighborhood n (Plane cs) = slice n $ fmap (slice n) cs | |
viewPlane pln = run $ each (pln # evaluate # neighborhood 5) | |
>< taskmap show | |
-- | |
type StreamT = NuT Opt | |
type Stream = StreamT Box | |
nums :: Stream Int | |
nums = coiter (\x -> just (x+1)) 0 | |
doubleIt :: Int -> Stream Int | |
doubleIt n = return $ n * 2 | |
squareIt :: Int -> Stream Int | |
squareIt n = return $ n * n | |
toList :: (Foldable t, Functor t, Monad m) | |
=> Int | |
-> t a | |
-> m (List a) | |
toList n ss = reduce (++) nil id $ each ss >< tasktake n >< taskmap single | |
-- | nice, informatively-named alias | |
stream :: Monad m => Stream a -> Source a m () | |
stream = toSource | |
stream_1 = nums >>= doubleIt >=> squareIt # toList 10 | |
-- Conway game! | |
-- | Extract the neighbors of a Plane's focus (a sub-plane) | |
neighbors :: List (Plane a -> Plane a) | |
neighbors = | |
horiz ++ vert ++ liftM2 (.) horiz vert where | |
horiz = cons moveLeft (cons moveRight nil) | |
vert = cons up (cons down nil) | |
-- | Count how many neighbors are alive | |
aliveNeighbors :: Plane Bool -> Int | |
aliveNeighbors z = | |
card $ map (\ dir -> extract $ dir z) neighbors | |
-- | Cardinality, ie number of True values in a list of booleans | |
card :: List Bool -> Int | |
card = length . filter (== True) | |
-- | A particular Conway rule | |
rule :: Plane Bool -> Bool | |
rule z = | |
case aliveNeighbors z of | |
2 -> extract z | |
3 -> True | |
_ -> False | |
evolve :: Plane Bool -> Plane Bool | |
evolve = extend rule | |
-- | Display helper for Cursors | |
dispLine :: Cursor Bool -> String | |
dispLine z = | |
toPrimList $ fmap dispC $ slice 6 z where | |
dispC True = '*' | |
dispC False = ' ' | |
-- | Display helper for Planes | |
disp :: Plane Bool -> String | |
disp (Plane z) = | |
unlines $ fmap dispLine $ slice 6 z | |
-- | Initial conditions to create a "glider" | |
glider :: Plane Bool | |
glider = makePlane f $ | |
cons ((cons f (cons t (cons f nil)))) $ | |
cons ((cons f (cons f (cons t nil)))) $ | |
cons ((cons t (cons t (cons t nil)))) nil | |
where f = False | |
t = True | |
-- | Now we will stream the iterations of the glider, potentially infinitely | |
glider_stream :: Stream (Plane Bool) | |
glider_stream = unfold (\g -> (g, just (evolve g))) glider | |
print_sink f n = loop (n :: Int) where | |
loop 0 = return () | |
loop n = do | |
it <- await | |
liftIO . putStrLn $ f it | |
loop (n - 1) | |
{- | | |
- Using this technique I have constructed a corecursive computation which I | |
- can evaluate an arbitrary number of times, receiving a new result each time. | |
-} | |
glider_task n = run $ stream glider_stream >< print_sink disp n | |
{- | | |
- Similarly I can take a cursor computation and loop over it endlessly, | |
- shifting the result value back to the input slot before each iteration. | |
-} | |
eval cursor = stream $ unfold ( \c -> | |
let r = evaluate c | |
c' = insert (const (extract (moveL r))) cursor | |
in (r, just c') ) cursor | |
cursor_loop n = run $ (eval cursor_2) >< print_sink (show . extract) n |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment