Skip to content

Instantly share code, notes, and snippets.

@adinapoli
Created May 6, 2014 09:13
Show Gist options
  • Save adinapoli/7a8620e4f9ac277ed2f5 to your computer and use it in GitHub Desktop.
Save adinapoli/7a8620e4f9ac277ed2f5 to your computer and use it in GitHub Desktop.
-- Sources:
-- http://www.haskellforall.com/2012/09/the-functor-design-pattern.html
-- http://www.haskell.org/haskellwiki/Functor
-- http://learnyouahaskell.com/functors-applicative-functors-and-monoids
-- Hide pre-existing Maybe and map function from Prelude.
import Prelude hiding (Maybe (..), map)
-- Import the Monadic composition operator.
import Control.Monad ((<=<))
-- Definition:
-- A functor transforms one category into another category.
-- We define a custom maybe data type. What this says is that Maybe
-- has a type constructor 'a' and is a union type that is either
-- Just a or Nothing.
data Maybe a = Just a | Nothing deriving(Show, Eq)
-- We can make it an instance of the functor typeclass. To do this
-- we must define fmap on our Maybe data type.
instance Functor Maybe where
fmap f Nothing = Nothing
fmap f (Just x) = Just (f x)
-- The type signature of fmap is:
--
-- fmap :: Functor f => (a -> b) -> f a -> f b
--
-- It takes a function (a -> b) and unwraps the value inside the functor,
-- applies the function and then re-wraps the value into the functor.
-- Functors must satisfy the following laws in order to be correct.
-- fmap id = id
-- fmap (p . q) = (fmap p) . (fmap q)
-- What this says is that passing the identity function to fmap should be
-- equivalent to calling the identity function on the functor data type and
-- the second of the two means that it is closed under composition.
justOne = Just 1
lawOne = fmap id justOne == id justOne
lawTwo = fmap ((* 2) . (* 3)) justOne == (fmap (* 2) . fmap (* 3)) justOne
-- Surprisingly, the Haskell world view of functors is really quite limited.
-- The functor type class focuses on the notion of functors as being
-- container types and the application of functions on them.
-- More generally, functors can be viewed as a mapping between two categories.
-- This could, for example, be the mapping between pure functions and monadic
-- functions.
-- Let's make our Maybe data type a Monad instance. Let's forget about the
-- details for now because we'll cover monads in week three.
instance Monad Maybe where
return x = Just x
val >>= f = case val of
Nothing -> Nothing
Just x -> f x
-- Here is our function that maps our pure function to the category of monadic
-- functions.
map :: (Monad m) => (a -> b) -> (a -> m b)
map f = return . f
-- An addition function in the category of pure functions.
pureAddOne :: Integer -> Integer
pureAddOne = (+ 1)
-- This allows us to write adapter functions that map our pure functions
-- into the category of monadic functions.
justTen = Just 10
result = justTen >>= (map pureAddOne)
-- We can even prove it is a functor by checking that it obeys to functor laws.
lawOne' = (justTen >>= map id) == id justTen
lawTwo' = (justTen >>= map ((* 2) . (* 3))) == (justTen >>= (map (* 2) <=< map (* 3)))
-- Therefore, we can say that the map we defined is a functor.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment