Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell Applicative [] why can I not replace pure[] with [] in function?

ghci> :t pure []
pure [] :: Applicative f => f [a]

ghci> pure []
[]

ghci> :t []
[] :: [a]

ghci> fmap ((:) 2) (pure [])
[2]

ghci> fmap ((:) 2) ([])
[]

I would have thought replacing pure[] with [] in fmap ((:) 2) (pure []) would result in the same outcome.. who can explain the difference to me?

like image 398
Roely de Vries Avatar asked Apr 05 '14 08:04

Roely de Vries


People also ask

What is pure applicative?

pure encapsulates a value into an arbitrary Applicative functor. Hence, pure 0 can mean any of: Just 0 , [0] , \_ -> 0 , (mempty, 0) , etc. Also note that return = pure .

What is applicative in Haskell?

Definition. In Haskell, an applicative is a parametrized type that we think of as being a container for data of that type plus two methods pure and <*> . Consider a parametrized type f a . The pure method for an applicative of type f has type. pure :: a -> f a.

What is pure Haskell?

From HaskellWiki. A function is called pure if it corresponds to a function in the mathematical sense: it associates each possible input value with an output value, and does nothing else.


2 Answers

The type of pure is Applicative f => a -> f a so pure []

has type Applicative f => f [a]. You can specify f explicitly for different applicative types e.g.

pure [] :: Maybe [String]   //Just []
pure [] :: IO [String]      //displays '[]' in ghci
pure [] :: [[String]]       //[[]]

The type of fmap ((:) 2) (pure []) is (Applicative f, Num a) => f [a]. Since Ghci runs in the IO monad, which is an Applicative, it chooses f to be IO, and displays the resulting list [2] which is what you see.

You can specify the applicative type directly to see a different result e.g.

fmap ((:) 2) (pure []) :: (Maybe [Int])

which is Just [2]

In fmap ((:) 2) ([]), there are no elements in the input list so you get an empty list.

like image 63
Lee Avatar answered Oct 04 '22 21:10

Lee


Why would it be the same? They're different expressions.

Perhaps it simplifies things if we just look at lists. So, there's the empty list []. Its type can be anything with brackets around it, in particular also a nested list:

Prelude> [] :: [[Int]]
[]

But there's also another "empty nested list":

Prelude> [[]] :: [[Int]]
[[]]

This one isn't really empty though, it merely contains only the empty list. ([[],[],[],...] would also be possible). In fact, that would be the result of what you've tried, if done in the list applicative:

Prelude Control.Applicative> pure [] :: [[Int]]
[[]]

It is certainly not the same as the empty list,

Prelude> [[]] == []
False

In particular,

Prelude> (length [], length [[]])
(0,1)

And fmappming over an empty list never does anything, while fmapping over an empty-containing list will call the function with []: an easy way to see this is

Prelude Control.Applicative> fmap undefined []
[]
Prelude Control.Applicative> fmap undefined [[]]
[* Exception: Prelude.undefined

The real confusing thing about your trials is that ghci silently uses the IO monad.


There's also a subclass of Applicative for actual emptiness:

Prelude Control.Applicative> :i Alternative
class Applicative f => Alternative f where
  empty :: f a
  (<‌|>) :: f a -> f a -> f a
  some :: f a -> f [a]
  many :: f a -> f [a]
  -- Defined in `Control.Applicative'
instance Alternative [] -- Defined in `Control.Applicative'
instance Alternative Maybe -- Defined in `Control.Applicative'

like image 42
leftaroundabout Avatar answered Oct 04 '22 22:10

leftaroundabout