Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does GHCi print partially-applied values created from "pure"?

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?

like image 428
Alexis King Avatar asked Mar 11 '15 04:03

Alexis King


2 Answers

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:

  1. As an IO action to execute.
  2. As a value to print out.

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.

like image 87
dfeuer Avatar answered Nov 08 '22 01:11

dfeuer


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 ().
like image 7
Ørjan Johansen Avatar answered Nov 08 '22 00:11

Ørjan Johansen