softshipper
softshipper

Reputation: 34061

Generalize the function

I am learning haskell and try to understand what does generalization mean in the functional programming.

Consider follow code snippet:

twiceLifted :: (Functor f1, Functor f) =>
               f (f1 a) -> f (f1 Char)
twiceLifted = (fmap . fmap) replaceWithP

twiceLifted' :: [Maybe String] -> [Maybe Char]
twiceLifted' = twiceLifted

As you can see, the first function has not concrete type, functor has to be implemented.

The second function looks more interesting. It reused the first function, but it has a concrete type.
So first function build the ground for the second function.
Is that what generalization in functional programming all about, reused something that already exists and build on top of it?

Upvotes: 1

Views: 512

Answers (1)

Anton Xue
Anton Xue

Reputation: 833

Generalization is about making concepts less restrictive. Consider a function that we might have, which goes through a [Int] and adds 5 to every element:

mapAdd5 :: [Int] -> [Int]
mapAdd5 [] = []
mapAdd5 (x:xs) = (x + 5) : mapAdd5 xs

Okay, what if we want to be able to perform more than just adding 5 to every element? What if we want go through a whole list and instead multiply every element by 2?

mapMult2 :: [Int] -> [Int]
mapMult2 [] = []
mapMult2 (x:xs) = (x * 2) : mapMult2 xs

Something you've probably noticed is that we are in essence applying a function of type Int -> Int to every element. Well?

mapInts :: (Int -> Int) -> [Int] -> [Int]
mapInts f [] = []
mapInts f (x:xs) = (f x) : mapInts f xs

Great, we've generalized away the internal function now. But what if we want to be able to apply this concept to a Double -> Double or even a String -> String?

mapMore :: (a -> a) -> [a] -> [a]
mapMore f [] = []
mapMore f (x:xs) = (f x) : mapMore f xs

What if we don't restrict the function to be of type a -> a. That is, we currently force the output type to be the same as the input type. What if they can be different, like a -> b? Although it might very well be the case that a is the same as b, but unlike a -> a, we don't force it to be so:

map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = (f x) : map f xs

And now we've generalized from mapAdd5 to map.

Bonus

What if we don't want to restrict ourselves to using lists? It turns out we can abstract / generalize lists into instances of Functor, which are merely things that enable functions to be applied on types that wrap / hold other items. Think about it, that's exactly what a list does!

fmap :: Functor f => (a -> b) -> f a -> f b

How is fmap defined? Its definition will vary based on what type of container f you have. However, in the case of lists (also known as []), we have it defined here:

instance Functor [] where
    fmap = map

Upvotes: 3

Related Questions