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.