5

I am currently studying Haskell with Prof. Hutton's "Programming in Haskell", and I found something strange regarding the definition of Maybe as an instance of the class Applicative.

In GHC.Base, the instance Applicative Maybe is defined as follows:

instance Applicative Maybe where
  pure = Just

  Just f  <*> m       = fmap f m
  Nothing <*> _m      = Nothing

It is the line which defines the value of Nothing <\*> _ as Nothing that bothers me. Nothing is of type Maybe a, where the operator <*> actually requires f (a -> b) (in this case, Maybe (a -> b)) as its first argument's type. Therefore, this is a type mismatch, which Haskell should complain about. However, this is accepted as a default definition, and therefore Haskell does not complain about it where I think it should.

What am I missing?

dfeuer
  • 48,079
  • 5
  • 63
  • 167
Namudon'tdie
  • 306
  • 1
  • 10

2 Answers2

11

The a in Maybe a is a type variable, and can be any type at all! So Nothing can have the type Maybe Int, or Maybe [x], or Maybe (p -> q), for example. Don't get confused by the fact that the variable name a is used in two places. The a in the type of Nothing is a completely different variable from the a in the type of <*>, and just happens to have the same name!

(That's exactly the same as if you wrote f x = x + 5 and then elsewhere, g x = "Hello, " ++ x. The use of x in both places doesn't matter, because they are in different scopes. Same with the a in this types. Different scopes, so they are different variables.)

Chris Smith
  • 2,615
  • 19
  • 18
  • Oh, I get it! So, 'a' in "Maybe a" is a very broad generalization of any type that can be given to the constructor function "Maybe". Thanks a lot! – Namudon'tdie Aug 13 '18 at 04:13
4

Let's make things clearer by relabeling a type variable:

Nothing :: Maybe x

The type Maybe x unifies with Maybe (a -> b), with x ~ (a -> b). That is, Nothing is a value that can used as Maybe a for any a, including a function type. Thus it is a legal left-hand argument for <*> here.

amalloy
  • 89,153
  • 8
  • 140
  • 205
  • Thank you for your answer. Small follow-up question here: should I read x ~ (a -> b) as "the variable x is associated with type (a -> b)"? – Namudon'tdie Aug 13 '18 at 04:15
  • @Namudon'tdie: `x ~ y` is a [type equality constraint](https://blog.infinitenegativeutility.com/2017/1/haskell-type-equality-constraints). Informally, people use it to mean “the type `x` is equal to the type `y`”; in Haskell, you can use this syntax as a typeclass constraint when certain extensions are enabled (e.g. `GADTs` or `TypeFamilies`). It’s mainly useful for controlling typeclass instance resolution and aiding type-level programming. – Jon Purdy Aug 13 '18 at 06:04