I am confused about the result of this expression:
fmap (*) 3
Which produces type:
fmap (*) 3 :: (Functor f, Num a, Num (f a)) => f (a -> a)
What I was expecting is that the result would be the same as doing:
(*) 3
Which produces a 1-parameter function:
Prelude> (*) 3 $ 10
30
I was expecting that fmap would apply 3 to (*) producing (*3), but the following produces an error:
Prelude> (fmap (*) 3) 10
<interactive>:201:1:
Non type-variable argument in the constraint: Num (a -> a)
(Use FlexibleContexts to permit this)
When checking that `it' has the inferred type
it :: forall a a1. (Num a, Num a1, Num (a1 -> a)) => a -> a
What type of parameter is the result of (fmap (*) 3) expecting?
1 Answer 1
The key is here:
fmap (*) 3 :: (Functor f, Num a, Num (f a)) => f (a -> a)
^^^^^^^^^
In Haskell numeric literals are overloaded, so 3 could have any type b as long as b has a Num instance.
The compiler goes through the following analysis:
- 3 has some type
bwith constraintNum b fmaphas typeFunctor f => (a -> c) -> f a -> f c- So
*has typea -> cand 3 has typef a *has typeNum a => a -> a -> asoc=a -> a, andahas constraintNum ab=f afrom (1) and (2)
Gathering up all of the constraints and equivalences:
fmap (*) 3 :: ( Functor f, -- by (2)
Num a, -- by (4)
Num (f a) -- by (1) and (5)
) => f ( a -> a ) -- by (2) and c = a -> a
Update:
You asked for an example where fmap (*) 3 makes sense. What is problematic here is that (*) has arity 2 and fmap is usually used with a function of a single argument (arity 1). For instance:
fmap (+1) (Just 3) = Just 4
fmap (+1) Nothing = Nothing
fmap (*6) [1,2,3] = [6,12,18]
fmap words getLine = do x <- getLine; return (words x)
Note: (+1) is just the "add 1" function, e.g. \x -> x+1, and (*6) is just the multiply by 6 function, e.g. \x -> x*6.
So fmapping (*) :: Int -> Int -> Int is somewhat unusual. Of course, we can interpret the type of (*) to be Int -> (Int -> Int) which gives it arity 1, but then dealing with functors of functions is getting into a level of abstraction that we don't normally deal with.
7 Comments
(fmap (*) 3) X where X is a value that does not cause the expression to end in error?fmap (*) 3 to be a function? It has type f (a -> a), which is not necessarily a function type. E.g., f could be [], making fmap (*) 3 a list of functions, not a function.Num instance for lists, like instance Num a => [a] where fromInteger x = [fromInteger x], then fmap (*) 3 would be fmap (*) [3], which is [(*) 3].
fmap (*) 3should be exactly the same as(*) 3? Thefmapfunction is clearly making some kind of a difference, and i don't imagine you expect that, say,filter (*) 3would be the same.fmapis a generalizedmapfunction, not at all like($).