{-# LANGUAGE KindSignatures #-} weirdListMap :: (a -> b) -> [a] -> [b] weirdListMap f as | length as < 3 = map f as | otherwise = [] -- Don't want to allow such things. Every Functor instance should -- satsify two laws: -- -- fmap id x = x -- (fmap id = id) -- -- fmap (f . g) = fmap f . fmap g -------------------------------------------------- -- Generalize to functions of multiple arguments? -- Can't implement this with only Functor constraint -- fmap2 :: Functor f => (a -> b -> c) -> f a -> f b -> f c -- fmap2 g fa fb = -- g :: a -> (b -> c) -- fa :: f a -- fb :: f b -- fmap :: (x -> y) -> f x -> f y -- fmap g fa :: f (b -> c) -- fmap (fmap g fa) fb -- doesn't type check, -- since (fmap g fa) :: f (b -> c), not (b -> c). -- if we had -- foo :: f (a -> b) -> f a -> f b -- then -- foo (fmap g fa) fb -- would work. -- In fact, foo is called (<*>) ("ap" or "splat") -- in the type class Applicative class Functor f => Applicative' (f :: * -> *) where pure' :: a -> f a (<*.>) :: f (a -> b) -> f a -> f b -- Applicative has several laws. Here's the most important: -- -- g :: a -> b -- x :: f a -- -- pure g <*> x :: f b -- -- So we must have fmap g x = pure g <*> x -- Examples! instance Applicative' Maybe where -- pure' :: a -> Maybe a -- (<*>.) :: Maybe (a -> b) -> Maybe a -> Maybe b -- pure' _ = Nothing ? pure' = Just (Just g) <*.> (Just a) = Just (g a) _ <*.> _ = Nothing -- Maybe models computations with potential failure. -- Applicative explains how function application works in the presence -- of potential failure. fmap2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c fmap2 g fa fb = (fmap g fa) <*> fb -- fmap2 g fa fb = (g `fmap` fa) <*> fb -- Applicative defines (<\$>) = fmap -- so we can write -- fmap2 g fa fb = g <\$> fa <*> fb