[0] https://hackage.haskell.org/package/base-4.20.0.1/docs/GHC-N...
[0] https://hackage.haskell.org/package/base-4.20.0.1/docs/GHC-N...
`a` is the type. Num is a `class`.
Here's an example. x is an Int32 and y is an Int64. If they had type Num, then this would be valid:
add :: Num -> Num -> Num -- Not valid Haskell
add x y = x + y
However it's not valid, because you can't add an Int32 and an Int64: add :: Int32 -> Int64 -> ? -- Doesn't compile
add x y = x + y
But you can add Nums together, as long as they're the same type. You indicate they're the same type by using the same type variable 'a': add :: a -> a -> a -- Doesn't compile
add x y = x + y
But now the above complains because you used (+) which belongs to Num, so you have to declare that these `a`s can (+) because they're Nums. add :: Num a => a -> a -> a
add x y = x + y
And it comes out shorter than your suggestion of putting the constraints afterward: add :: (a :: Num) -> (a :: Num) -> (a :: Num) -- Not valid Haskell
add x y = x + yThere's also the fact that `Num` is technically not a type, but a type class, which is like a level above a type: values are organized into types, and types are organized into classes. Though this is more of a limitation of Haskell: conceptually, type classes are just the types of types, but in practice, the way they're implemented means they can't be treated in a uniform way with ordinary types.
So that's why there's a syntactic distinction between `Num a` and `a :: Num`. As for why `Num` comes before `a`, there's certainly a reasonable argument for making it come after, given that we'd read it in English as "a is a Num". I think the reason it comes before is that it's based on the usual function call syntax, which is `f x` in Haskell (similar to `f(x)` in C-style languages, but without requiring the parentheses). `Num` is kind of like a function you call on a type which returns a boolean.