5
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?

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
Roely de Vries
  • 213
  • 1
  • 4

2 Answers2

15

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.

Lee
  • 142,018
  • 20
  • 234
  • 287
5

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'

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319