4

I am playing with Control.Applicative and I am realizing I don't understand everything with the Haskell type system.

Here is my experiment in Ghci:

λ :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

λ :t (<*>) (pure 2)
(<*>) (pure 2) :: (Num (a -> b), Applicative f) => f a -> f b

The type of the first argument of <*> is f (a -> b).

  • Why is this expression correct?
  • How can it be unified with (pure 2) since the constant 2 is not of type a -> b?
  • What does Num (a -> b) mean? How can a function having a a -> b type be an instance of Num?
z1naOK9nu8iY5A
  • 903
  • 7
  • 22
  • 9
    `Num (a -> b)` in the context doesn't mean there *is* a `Num` instance for `a -> b`. It means "if there *were*" a `Num` instance for `a -> b` then the expression would have this type. – Tom Ellis Dec 02 '14 at 11:12
  • 1
    In addition, the "constant" is a literal, and therefore has type `2 :: Num a => a`. – Zeta Dec 02 '14 at 11:12
  • 1
    try next: `:t (<*>) (pure (2 :: Int))` – viorior Dec 02 '14 at 14:57

1 Answers1

7

The first argument of <*> is supposed to be f (a -> b). So given (<*>) (pure x), this is well-typed provided that x is some kind of function.

The type of 2 is Num a => a. In other words, 2 can be any possible type, so long as it's an instance of Num.

So in your expression (<*>) (pure 2), this is well-typed provided that the type of 2 is a function type, and that function type has a Num instance.

Of course, there is almost no reason why you would ever want a function to have a Num instance. But the compiler doesn't know that. All it's saying is that if there was such an instance, then the expression would become well-typed.

(This is similar to the error you sometimes see where the compiler wants some type to be an instance of Integral and Fractional simultaneously. To a human, this is a nonsensical combination. To a machine, they're just two ordinary classes...)

MathematicalOrchid
  • 61,854
  • 19
  • 123
  • 220
  • 1
    Having functions behave like numbers is actually quite useful sometimes. It's not a standard instance, but it makes perfect sense. Something like `instance (Num b) => Num (a -> b) where f + g = \ x -> f x + f y` etc. This allows you to write things like `(sin + cos) 0.1` with the obvious meaning. – augustss Dec 02 '14 at 13:22
  • How could be ```2``` be of a function type? How would you call it? Are number literals part of the Num class per definition? – z1naOK9nu8iY5A Dec 02 '14 at 13:33
  • @augustss I did say "almost". ;-) – MathematicalOrchid Dec 02 '14 at 13:36
  • 1
    Remember, `2` is just syntax for some instance of Num which is how you can treat it as Int or Double with abandon. In the case of `Num b => Num (a -> b)`, 2 becomes the function which is "constantly 2", or `const 2`. – J. Abrahamson Dec 02 '14 at 13:39
  • Thanks. May you refer me to a doc or spec explaining that in details? I would never have thought it would be interpreted as ```const 2``` – z1naOK9nu8iY5A Dec 02 '14 at 13:41
  • 2
    It isn't by default, but it is acceptable (read: law-abiding) to do so. The fragment of code augustss gave above is a valid (if partial) `instance Num b => Num (a -> b)`. The relevant part you're asking for would look like `fromInteger a = const (fromInteger a)`. In relation to `Applicative`s you can see this as nothing more than lifting the `Num` methods into the `Reader` `Applicative`, `type Reader a b = (a -> b)`. For instance, `fromInteger a = pure (fromInteger a)` and `(+) = liftA2 (+)`. – J. Abrahamson Dec 02 '14 at 14:46
  • 1
    @z1naOK9nu8iY5A Look at http://stackoverflow.com/questions/26515102/making-numeric-functions-an-instance-of-num – augustss Dec 02 '14 at 14:47
  • 1
    @z1naOK9nu8iY5A Also, https://gist.github.com/tel/e1f040a4e42887e6ab7a to clarify that `Applicative` nonsense I was talking about in my previous comment. – J. Abrahamson Dec 02 '14 at 14:51