Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the 2-tuple Functor instance only apply the function to the second element?

import Control.Applicative  main = print $ fmap (*2) (1,2) 

produces (1,4). I would expect it it to produce (2,4) but instead the function is applied only to the second element of the tuple.

Update I've basically figured this out almost straight away. I'll post my own answer in a minute..

like image 782
Peter Hall Avatar asked Nov 18 '12 17:11

Peter Hall


People also ask

What is functor in Haskell?

Functor in Haskell is a kind of functional representation of different Types which can be mapped over. It is a high level concept of implementing polymorphism. According to Haskell developers, all the Types such as List, Map, Tree, etc. are the instance of the Haskell Functor.

What are functor laws?

Functor Laws If two sequential mapping operations are performed one after the other using two functions, the result should be the same as a single mapping operation with one function that is equivalent to applying the first function to the result of the second.

Why is set not a functor?

Mapping a set doesn't preserve those structures, and that's the reason that sets aren't functors.

What is pure Haskell?

The pure method means you always have a way to take an ordinary type and put it in a context. Because of pure , you can take any value that's not in a context and trivially put it in one. This is vital to allowing all possible computations in a context.


2 Answers

Let me answer this with a question: Which output do you expect for:

main = print $ fmap (*2) ("funny",2) 

You can have something as you want (using data Pair a = Pair a a or so), but as (,) may have different types in their first and second argument, you are out of luck.

like image 145
Landei Avatar answered Oct 18 '22 22:10

Landei


Pairs are, essentially, defined like this:

data (,) a b = (,) a b 

The Functor class looks like this:

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

Since the types of function arguments and results must have kind * (i.e. they represent values rather than type functions that can be applied further or more exotic things), we must have a :: *, b :: *, and, most importantly for our purposes, f :: * -> *. Since (,) has kind * -> * -> *, it must be applied to a type of kind * to obtain a type suitable to be a Functor. Thus

instance Functor ((,) x) where   -- fmap :: (a -> b) -> (x,a) -> (x,b) 

So there's actually no way to write a Functor instance doing anything else.


One useful class that offers more ways to work with pairs is Bifunctor, from Data.Bifunctor.

class Bifunctor f where   bimap :: (a -> b) -> (c -> d) -> f a c -> f b d   bimap f g = first f . second g    first :: (a -> b) -> f a y -> f b y   first f = bimap f id    second :: (c -> d) -> f x c -> f x d   second g = bimap id g 

This lets you write things like the following (from Data.Bifunctor.Join):

  newtype Join p a =     Join { runJoin :: p a a }    instance Bifunctor p => Functor (Join p) where     fmap f = Join . bimap f f . runJoin 

Join (,) is then essentially the same as Pair, where

data Pair a = Pair a a 

Of course, you can also just use the Bifunctor instance to work with pairs directly.

like image 45
dfeuer Avatar answered Oct 19 '22 00:10

dfeuer