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