module Chapter17 where
import Control.Applicative
import Data.List (elemIndex)
import Data.Semigroup
-- Applicatives are monoidal functors
main :: IO ()
main = putStrLn "Hello Applicative"
-- fmap
-- ($) :: (a -> b) -> a -> b
-- (<$>) :: Functor f => (a -> b) -> f a -> f b
-- (<*>) :: Applicative f => f (a -> b) -> f a -> f b
-- Control.Applicative supplies
-- note that its CLOSE to, but distinct from, fmap
-- the structure is an applicative rather than a functor
-- liftA :: Applicative f => (a -> b) -> f a -> f b
-- liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
-- liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
--
-- :t pure :: Applicative a => a -> f a
-- So pure isn't about wrapping a function in an applicative structure
-- It's about wrapping a value into an applicative structure
-- And fuctions happen to be values
-- And its not about creating arbitrary structure
-- so pure 1 :: ([a], Int)
-- is going to return ([], 1)
-- Because it is about preserving structure, because it is a functor
-- pure is mempty for Applicative??
-- Are "applicative functor" and "monoidal functor" the same thing?
--
-- Woah:
-- mappend :: f f f
-- $ :: (a -> b) a b
-- (<*>) :: (a -> b) -> f a -> f b
-- We have a Monoid for our structure and function application for our values!
--
-- Uses I can think of:
-- * If you fail to create a function, as in if you Maybe return a function
--
-- Monoid and Applicative instances are not required or guaranteed to have the same monoid of structure, and the functorial part may change the way it behaves.
--
-- Seems like a rule of thumb is if you could mappend OR apply a function, prefer applying the function.
-- And mappending as a back up where it doesn't make sense??
--
-- (<*>) :: f (a -> b) -> f a -> f b
-- (<*>) :: [] (a -> b) -> [] a -> [] b
-- (<*>) :: [] (a -> b) -> [a ] -> [b]
-- <*> is called "apply"?
--
f x = lookup x [(3, "hello"), (4, "julie"), (5, "kbai")]
g y = lookup y [(7, "sup?"), (8, "chris"), (9, "aloha")]
h z = lookup z [(2, 3), (5, 6), (7, 8)]
m x = lookup x [(4, 10), (8, 13), (1, 9001)]
-- Exercises: Lookups
-- 1.
lookupOne :: Maybe Integer
lookupOne = (+ 3) <$> lookup 3 (zip [1 .. 3] [4 .. 6])
-- 2.
lookupTwoY :: Maybe Integer
lookupTwoY = lookup 3 $ zip [1 .. 3] [4 .. 6]
lookupTwoZ :: Maybe Integer
lookupTwoZ = lookup 2 $ zip [1 .. 3] [4 .. 6]
lookupTwoTupled :: Maybe (Integer, Integer)
lookupTwoTupled = pure (,) <*> lookupTwoY <*> lookupTwoZ
-- 3.
lookupThreeX :: Maybe Int
lookupThreeX = elemIndex 3 [1 .. 5]
lookupThreeY :: Maybe Int
lookupThreeY = elemIndex 4 [1 .. 5]
lookupThreeMax' :: Int -> Int -> Int
lookupThreeMax' = max
lookupThreeMaxed :: Maybe Int
lookupThreeMaxed = pure lookupThreeMax' <*> lookupThreeX <*> lookupThreeY
-- 4.
lookupFourXs = [1 .. 3]
lookupFourYs = [4 .. 6]
lookupFourX :: Maybe Integer
lookupFourX = lookup 3 $ zip lookupFourXs lookupFourYs
lookupFourY :: Maybe Integer
lookupFourY = lookup 2 $ zip lookupFourXs lookupFourYs
summed :: Maybe Integer
summed = pure sum <*> (pure (,) <*> lookupFourX <*> lookupFourY)
--
-- f ~ Identity
-- Applicative f =>
-- type Id = Identity
-- (<*>) :: f (a -> b) -> f a -> f b
-- (<*>) :: Id (a -> b) -> Id a -> Id b
--
-- pure :: a -> f a
-- pure :: a -> Id a
newtype Identity a =
Identity a
deriving (Eq, Ord, Show)
instance Functor Identity where
fmap f (Identity a) = Identity (f a)
instance Applicative Identity
-- pure a = Identity a
where
pure = Identity
-- (<*>) (Identity f) thingThatsAFunctor = fmap f thingThatsAFunctor
(<*>) (Identity f) = fmap f
-- failed attempts:
-- (<*>) f thingThatsAFunctor = pure f <*> pure thingThatsAFunctor
-- (<*>) f thingThatsAFunctor = pure (f thingThatsAFunctor)
-- (<*>) f thingThatsAFunctor = (f thingThatsAFunctor)
-- (<*>) f thingThatsAFunctor = (fmap f thingThatsAFunctor)
-- (<*>) f fA = fmap f fA
-- (<*>) f (Identity a) = f <*> (Identity a)
--
newtype Constant a b =
Constant
{ getConstant :: a
}
deriving (Eq, Show)
type C = Constant
-- (<*>) :: f (a -> b) -> f a -> f b
-- (<*>) :: C e (a -> b) -> C e a -> C e b
--
-- pure :: a -> f a
-- pure :: a -> C e a
instance Functor (Constant a) where
fmap _ (Constant a) = Constant a
instance (Semigroup a) => Semigroup (Constant a b) where
(<>) (Constant x1) (Constant x2) = Constant (x1 <> x2)
instance (Semigroup a, Monoid a) => Monoid (Constant a b) where
mempty = mempty
mappend = (<>)
instance (Semigroup a, Monoid a) => Applicative (Constant a) where
pure x = Constant mempty
(<*>) (Constant x0) (Constant x) = Constant (x0 <> x)
validateLength :: Int -> String -> Maybe String
validateLength maxLen s =
if length s > maxLen
then Nothing
else Just s
newtype Name =
Name String
deriving (Eq, Show)
newtype Address =
Address String
deriving (Eq, Show)
mkName :: String -> Maybe Name
mkName s = Name <$> validateLength 25 s
mkAddress :: String -> Maybe Address
mkAddress a = Address <$> validateLength 100 a
data Person =
Person Name Address
deriving (Eq, Show)
mkPerson :: String -> String -> Maybe Person
mkPerson n a =
case mkName n of
Nothing -> Nothing
Just n' ->
case mkAddress a of
Nothing -> Nothing
Just a' -> Just $ Person n' a'
mkPerson' :: String -> String -> Maybe Person
mkPerson' n a = pure Person <*> mkName n <*> mkAddress a
mkPerson'' :: String -> String -> Maybe Person
mkPerson'' n a = Person <$> mkName n <*> mkAddress a
data Cow =
Cow
{ name :: String
, age :: Int
, weight :: Int
}
deriving (Eq, Show)
noEmpty :: String -> Maybe String
noEmpty "" = Nothing
noEmpty str = Just str
noNegative :: Int -> Maybe Int
noNegative x
| x >= 0 = Just x
| otherwise = Nothing
makeCow :: String -> Int -> Int -> Maybe Cow
makeCow name age weight =
Cow <$> noEmpty name <*> noNegative age <*> noNegative weight
makeCow' :: String -> Int -> Int -> Maybe Cow
makeCow' name age weight =
liftA3 Cow (noEmpty name) (noNegative age) (noNegative weight)
maybeApply :: Maybe (a -> b) -> Maybe a -> Maybe b
maybeApply = (<*>)
maybeMap :: (a -> b) -> Maybe a -> Maybe b
maybeMap = fmap
-- maybeMapBad :: (a -> b) -> Maybe a -> f b
-- maybeMapBad = fmap
-- rigid type variable is a funny name to me,
-- because it means it can be any type at all, which doesn't sound rigid
-- but it makes sense because when you get this error, you are forcing it to be a certain type or
-- at least follow certain constraints, like Num a, or Maybe a
--
-- Exercise: Fixer Upper
-- 1.
fixerUpperOne = const <$> Just "Hello" <*> pure "World"
-- 2.
fixerUpperTwo =
fmap (,,,) (Just 90) <*> Just 10 <*> Just "Tierness" <*> pure [1, 2, 3] -- Applicative Laws
-- Law 1: Identity
-- pure id <*> v = v
-- Law 2: Composition
-- pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
-- Law 3: Homomorphism
-- pure f <*> pure x = pure (f x)
-- Law 4: Interchange
-- u <*> pure y = pure ($ y) <*> u
--