4

In Haskell, the class Functor is declared as:

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

Can type variables a and b be function types, or must they be non-function types?

If they can be function types, isn't it that class Functor become effectively the same as class Applicative, in terms of making fmap able to apply to functions with arbitrary number of arguments? According to what Programming in Haskell by Hutton says:

Functors abstract the idea of fmap mapping a function over each element of a structure. Applicatives generalize this idea to allow fmap mapping functions with any number of arguments to be mapped, rather than being restricted to functions with a single argument.

In applicative:

fmap0 ::  a   ->  f   a
fmap0 =   pure
fmap1 ::  (a  ->  b)  ->  f   a   ->  f   b
fmap1 g   x   =   pure    g   <*> x
fmap2 ::  (a  ->  b   ->  c)  ->  f   a   ->  f   b   ->  f   c
fmap2 g   x   y   =   pure    g   <*> x   <*> y
fmap3 ::  (a  ->  b   ->  c   ->  d)  ->  f   a   ->  f   b   ->  f   c   ->  f   d
fmap3 g   x   y   z   =   pure    g   <*> x   <*> y   <*> z

Class Applicative is declared as:

class Functor f   =>  Applicative f   where
pure  ::  a   ->  f   a
(<*>) ::  f   (a  ->  b)  ->  f   a   ->  f   b

Thanks.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
Tim
  • 1
  • 141
  • 372
  • 590
  • 4
    `a` and `b` can be function types. – Willem Van Onsem Jul 17 '19 at 23:03
  • Then what can applicatives do that functors can't? See my update – Tim Jul 17 '19 at 23:06
  • An applicative needs to have a `pure` thing, something to "wrap it in an `Applicative`). It is not said that this is possible. – Willem Van Onsem Jul 17 '19 at 23:10
  • What does "It is not said that this is possible" mean? I am also still wondering about my previous comment. – Tim Jul 17 '19 at 23:12
  • 3
    Your implementation of `fmap1` uses `Applicative` methods. This shows that just having `Applicative` is enough to implement `Functor`. But your question asks the other way: is just having `Functor` enough to implement `Applicative`? I think if you try to go the other way -- try to write down implementations of `pure` and `(<*>)` just using `fmap` (and pure things) on the right-hand side -- you will find yourself stuck quite quickly indeed. – Daniel Wagner Jul 18 '19 at 00:01
  • 1
    See also: [Good examples of Not-a-Functor/Functor/Applicative/Monad](https://stackoverflow.com/q/7220436/791604)? – Daniel Wagner Jul 18 '19 at 00:02
  • 1
    The fact that `b` can be a function type is why `Applicative` exists: `fmap (+) (Just 3) == Just (+3)`. `Applicative` is what lets you apply the result of a partial application of `+` to another `Just` value. `fmap (+) (Just 3) <*> Just 5 == Just (+3) <*> Just 5 == Just 8`. – chepner Jul 18 '19 at 11:59

3 Answers3

12

Can type variables a and b be function types

– yeah, sure.

isn't it that class Functor become effectively the same as class Applicative

no, absolutely not. If you insert a function type into the a or b of the fmap signature, you get things like

fmap :: ((x -> y) -> b) -> f (x -> y) -> f b

or

fmap :: (a -> p -> q) -> f a -> f (p -> q)

but crucially, fmap always takes exactly one f _ wrapped-value and spits out exactly one such value. Applicative meanwhile allows you to accept any number of wrapped values, provided you give it the a function to process the contained values.

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • Thanks. In my post, is it correct that `fmap0` and `fmap1` form a endofunctor on the category of types? What do `fmap0`, `fmap1`, `fmap2`, ... form? – Tim Jul 20 '19 at 21:33
  • No. Functors are formed by a mapping of objects (i.e. types) and one of morphisms (i.e. of functions). `fmap1` is indeed a mapping of functions, but `fmap0` is a mapping of _values_, not of types. In the `Functor` class, the mapping of objects is implicit: it's the type constructor `f` itself. – leftaroundabout Jul 20 '19 at 21:51
  • https://stackoverflow.com/a/33442135/745903, also https://stackoverflow.com/questions/21647659/why-functor-class-has-no-return-function – leftaroundabout Jul 20 '19 at 21:55
11

a and b can be function types. They can be any type. In fact, a valid Functor must allow them to be any type.

To answer your Applicative question, let's try it.

fmap :: (a -> b -> c) -> f a -> f (b -> c)

Okay, great! Now I can take an f a and convert it to a f (b -> c). But.... then what? I can't apply f (b -> c) to an argument. It's not a function; it's a value of my functor type. If only we had a function with this signature...

superFmap :: f (b -> c) -> f b -> f c

But that sure does look a lot like

(<*>) :: f (b -> c) -> f b -> f c

which is a member of Applicative. Hence, we need Applicative to be able to apply this secondary result.

What the other answers said is correct. We can't implement pure either, for similar reasons. But it's important to note that we can't even get (<*>) in general, because if we could then that would imply that every Functor is Apply, which is also certainly not the case.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
5

Can type variables a and b be function types, or must they be non-function types?

a and b can be any type, so function types as well.

For example we can use:

fmap (+) [1,4,2,5]

Here (+) has type:

fmap :: Functor f => (a -> b       ) -> f a -> f b
(+)  :: Num c =>      c -> (c -> c)

So here b ~ c -> c, and b is thus a function.

An example where a is a function is:

fmap ($ 2) [ (1+), (2*), (3-) ]

here we have as types:

fmap :: Functor f => (a        -> b) -> f a -> f b
($ 3)  :: Num c =>    (c -> d) -> d

So a ~ (c -> d) here, here we thus apply 3 to all functions in the list.

Note that we did not add extra fmap definitions here (as in fmap1, fmap2, ...). It is just the fact that a can be substituted with a type c -> d, etc.).

If they can be function types, isn't it that class Functor become effectively the same as class Applicative.

No, since it is for example not said that pure can be implemented for a given Functor. Immagine that you for example make a data type:

type Owner = String

data Owned a = Owned Owner a

then Owned can be implemented to be an instance of Functor:

instance Functor Owned where
    fmap f (Owned o x) = Owned o (f x)

But implementing a pure :: a -> Owned a is not possible: why would be the owner of the object?

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555