- Use Stack to create a new project working-with-functors with the simple template:
stack new working-with-functors simple
- Open src/Main.hs in the editor. We will use this file to demonstrate the usage of Functors.
- After initial module definition for Main, import the module that includes the Functor type class:
import Data.Functor
- Define a function to square a number. We will use it to demonstrate application of this function over several data structures:
-- Square a number
square :: Num a => a -> a
square x = x * x
- Functor f is a type class that needs fmap :: (a -> b) -> f a -> f a. Data.Functor defines a function <$> synonymous to fmap. List defines an instance for Functor. We will use a square function to apply over a list. Add the following code to get a square of all the elements in the list:
-- Mapping a list
squareList :: Num a => [a] -> [a]
squareList xs = square <$> xs
- Similarly, we can use <$> to apply over Maybe and Either data types. Maybe allows a function to be applied if the data is represented with Just. Otherwise, a function is not applied. The Either instance for Functor allows a function to be applied only when the Right constructor is used:
-- Mapping a Maybe
squareMaybe :: Num a => Maybe a -> Maybe a
squareMaybe x = square <$> x
-- Mapping an Either
squareEither :: Num a => Either c a -> Either c a
squareEither x = square <$> x
- Now, define a data type Function a b to represent a function a -> b. We will define this to be an instance of Functor. The Functor instance for this data type will create a composition by using the composition function (.):
data Function a b = Function (a -> b)
instance Functor (Function a) where
f `fmap` (Function g) = Function (f . g)
- Define another utility function double to double a given value. We will use it in the main function to demonstrate the function's composition:
double :: Num a => a -> a
double x = x + x
- Now, add the main function where we will put to test all the preceding definitions:
main :: IO ()
main = do
putStrLn "Mapping a list"
putStrLn $ show $ squareList [1..10]
putStrLn ""
putStrLn "Mapping Maybe"
putStrLn "Just 10 -> Just 100"
putStrLn $ show $ squareMaybe (Just 10)
putStrLn ""
putStrLn "Nothing -> Nothing"
putStrLn $ show $ squareMaybe Nothing
putStrLn ""
putStrLn "Mapping Either"
putStrLn "Right 10 -> Right 100"
putStrLn $ show $ squareEither (Right 10 :: Either String Int)
putStrLn "Left String -> Left String"
putStrLn $ show $ squareEither (Left "Left Value" ::
Either String Int)
let squareF = Function square
doubleSquare = double <$> squareF
-- Take the resultant function out of doubleSquare
let Function dsq = doubleSquare
putStrLn "Double the Square of X"
print $ dsq 10
- Build and run the project:
stack build
stack exec -- working-with-functors
- The output should look like this: