I've been playing around with Applicative instances in order to figure out how they work. However, I honestly don't understand this behavior.
If I define my own datatype, then apply pure to it with no other arguments, nothing prints out, but it errors if I try to apply something to the result.
ghci> data T = A
ghci> pure A
ghci> pure A 0
<interactive>:21:1:
No instance for (Show T) arising from a use of ‘print’
In a stmt of an interactive GHCi command: print it
However, if I make T an instance of Show, then A is printed out in both cases.
ghci> data T = A deriving (Show)
ghci> pure A
A
ghci> pure A 0
A
What I really don't understand is how pure A can be a value that is printed differently between the two cases. Isn't pure A partially applied?
I do understand why calling pure A 0 errors in the first example and doesn't in the second—that makes sense to me. That's using the ((->) r) instance of Applicative, so it simply yields a function that always returns A.
But how is pure instantiated with only one value when the type of the applicative itself isn't yet known? Furthermore, how can GHC possibly print this value?
GHCi is a little bit peculiar. In particular, when you type an expression at the prompt, it tries to interpret it in two different ways, in order:
IO action to execute.Since IO is Applicative, it is interpreting pure A as an IO action producing something of type T. It executes that action (which does nothing), and since the result is not in Show, it does not print anything out. If you make T an instance of Show, then it kindly prints out the result for you.
When you write pure A 0, GHCi sees this:
pure :: Applicative f => a -> f a
pure A :: Applicative f => f T
And since you apply pure A to 0, pure A must be a function a->b for some types a and b, and a must contain 0.
(Num a, Applicative f) => f T ~ (a -> b)
(Note that x ~ y means that x and y unify—they can be made to have the same type.)
Thus we must have f ~ ((->) a) and T ~ b, so in fact GHC infers that, in this context,
pure A :: Num a => ((->) a) T
Which we can rewrite as
pure A :: Num a => a -> T
Well, (->) a is an instance of Applicative, namely "reader", so this is okay. When we apply pure A to 0 we get something of type T, namely A. This cannot be interpreted as an IO action, of course, so if T is not an instance of Show, GHCi will complain.
When you give a value of ambiguous type to the GHCi prompt to evaluate, it tries to default the type in various ways. In particular, it tries whether it can fit an IO a type, in case you want to execute an IO action (see the GHC manual). In your case, pure A defaults to the type IO T. Also:
Furthermore, GHCi will print the result of the I/O action if (and only if):
- The result type is an instance of
Show.- The result type is not
().
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With