4

This question arose while reading the new chapter in the excellent Learn You a Haskell about applicative functors.

The Applicative typeclass has, as part of the definition for the Maybe instance:

pure = Just

If I just go to GHCi and import Control.Applicative, and do:

pure (3+)

I don't get Just anything (makes sense). But if I use it in part of the expression:

pure (3+) <*> Just 4

I get Just 7. I guess it also isn't surprising, but I'm missing something integral about how typeclasses work, I think, that there is no ambiguity with the call to pure here.

If my confusion makes sense, can anyone explain what's going on in detail?

Tom Lokhorst
  • 13,658
  • 5
  • 55
  • 71
J Cooper
  • 16,891
  • 12
  • 65
  • 110

4 Answers4

7

It's just type inference. The (<*>) operator requires both arguments to use the same Applicative instance. The right side is a Maybe, so the left side has to be a Maybe also. So that's how it figures out which instance is used here. You can look at the type of any expression in the interpreter by typing :t expression, and maybe if you just go through each subexpression and look at the type that was inferred, you will get a better picture of what's going on.

newacct
  • 119,665
  • 29
  • 163
  • 224
  • Ah, so does the compiler do something like "Hmm, this argument's type is ambiguous, so let me check the next argument's type and come back to it"? – J Cooper May 03 '09 at 19:36
  • 9
    Type inference is quite complex, but it's worth knowing that it doesn't happen in one step. The type inferencer usually collects some information in one step, and at a later stage some more information. So it doesn't just infer the correct types for everything in one go, from left to right. In this case, it will infer that `pure` is of type `(Applicative a1) => a1 (Int -> Int)` where the `a1` is just a made up type variable, at a later stage during type inference it will conclude that `a1` must be `Maybe`, and then it will substitute `Maybe` for `a1` everywhere. – Tom Lokhorst May 03 '09 at 20:11
  • Thank you -- that is quite helpful! – J Cooper May 03 '09 at 22:22
3

It's worth looking at the type the compiler infers for pure (3+):

Prelude Control.Applicative> :t pure (3+)
pure (3+) :: (Num a, Applicative f) => f (a -> a)

The type of this term is overloaded, and the decision about the numeric class and the applicative class is delayed until later. But you can force a particular type with an annotation, for example:

*Showfun Control.Applicative> pure (3+) :: Maybe (Double -> Double)
Just <function>

(This works because Showfun has an instance declaration that prints a function value as <function>.)

It's just a question of when the compiler has accumulated enough information to make a decision.

Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533
  • Out of curiosity, is the `Showfun` module in some existing package, or is is just something you wrote yourself? The implementation is straightforward enough: `instance Show (a -> b) where; show _ = ""` – Tom Lokhorst May 04 '09 at 09:25
  • I wrote Showfun myself so I could get the example `pure (3+)` to print using `Just`. – Norman Ramsey May 05 '09 at 01:55
  • ShowFunctions is in QuickCheck now, I believe. – porges Aug 26 '09 at 04:50
2

To expand a bit on newacct's answer, if there isn't enough information to infer the actual type, the compiler may (in some cases) attempt to select a default type, limited to those that would satisfy the type constraints in question. In this case, the type inferred is IO (n -> n) for some difficult-to-determine instance of Num => n. GHCi then evaluates it and throws away the return value, with no visible effect.

bdonlan
  • 224,562
  • 31
  • 268
  • 324
1

Here is an interesting SO thread on type inference. Not Haskell specific, but lots of good links and stuff to read about type inference in functional languages.

Community
  • 1
  • 1
JP Alioto
  • 44,864
  • 6
  • 88
  • 112