简体   繁体   中英

Haskell function composition and fmap f

I have two simple examples:

1) xt function (what is this?)

Prelude> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
Prelude> :{
Prelude| f::Int->Int
Prelude| f x = x
Prelude| :}
Prelude> xt = fmap f // ?
Prelude> :t xt
xt :: Functor f => f Int -> f Int
Prelude> xt (+2) 1
3

2) xq function (via composition)

Prelude> :{
Prelude| return x = [x]
Prelude| :}
Prelude> xq = return . f
Prelude> :t xq
xq :: Int -> [Int]
Prelude> :t return
return :: a -> [a]

xq function I get through composition return(f(x)) . But what does that mean: fmap f and what is difference?

The Functor instance for (->) r defines fmap to be function composition:

fmap f g = f . g

Thus, xt (+2) == fmap f (+2) == f . (+2) == (+2) xt (+2) == fmap f (+2) == f . (+2) == (+2) (since f is the identity function for Int ). Applied to 1, you get the observed answer 3.


fmap is the function defined by the Functor type class:

class Functor f where
    fmap :: (a -> b) -> f a -> f b

It takes a function as its argument and returns a new function "lifted" into the functor in question. The exact definition is supplied by the Functor instance. Above is the definition for the function functor; here for reference are some simpler ones for lists and Maybe :

instance Functor [] where
    fmap = map

instance Functor Maybe where
    fmap f Nothing = Nothing
    fmap f (Just x) = Just (f x)

> fmap (+1) [1,2,3]
[2,3,4]
> fmap (+1) Nothing
Nothing
> fmap (+1) (Just 3)
Just 4

Since you can think of functors as boxes containing one or more values, the intuition for the function functor is that a function is a box containing the result of applying the function to its argument. That is, (+2) is a box that contains some value plus 2. (F)mapping a function on that box provides a box that contains the result of applying f to the result of the original function, ie, produces a function that is the composition of f with the original function.

Both xq = return . f xq = return . f and xt = fmap f can be eta-expanded :

xq x = (return . f) x = return (f x) = return x

Now it can be eta-contracted :

xq = return

The second is

xt y = fmap f y = fmap (\x -> x) y = fmap id y = id y = y

fmap has type :: Functor f => (a -> b) -> fa -> fb so fmap f has type :: Functor f => f Int -> f Int , because f :: Int -> Int . From its type we see that fmap f is a function, expecting an Int , and producing an Int .

Since fx = x for Int s by definition, it means that f = id for Int s, where id is a predefined function defined just the same way as f is (but in general, for any type).

Then by Functor laws (and that's all we need to know about "Functors" here) , fmap id = id and so xt y = y , in other words it's also id - but only for Int s,

xt = id :: Int -> Int

Naturally, xt (+2) = id (+2) = (+2) .


Addendum: for something to be a "Functor" means that it can be substituted for f in

fmap id (x :: f a) = x
(fmap g . fmap h)  = fmap (g . h)

so that the expressions involved make sense (ie are well formed, ie have a type), and the above equations hold (they are in fact the two "Functor laws").

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM