Why does Haskell use arrows for function type?

I was just starting to learn Haskell, and one of the weird things for me is the syntax for a function type with several arguments.

Consider a simple example:

(+) :: Num a => a -> a -> a 

What are all the arrows for? Doesn't it make sense to write something like Num Num Num -> Num ?

What is the reason under the hood? I searched for this question but did not find anything useful.

+7
syntax haskell
source share
2 answers

The first thing that confuses things is Num a => , so we will ignore it now. Instead, consider Int -> Int -> Int , which is one of the possible specializations of the type signature you gave.

Functions are almost always curried in Haskell. This means that a function with multiple arguments is actually a function of one argument, which returns a function that takes the next argument, etc.

-> is the correct associative, so Int -> Int -> Int is the same as Int -> (Int -> Int) .

It also means that this definition

 f :: Int -> Int -> Int fxy = x + y 

coincides with

 f :: Int -> Int -> Int fx = \y -> x + y 

In fact, all functions in Haskell take exactly one argument. Tuples also exist, but they are first-class citizens, so they are more than just a list of arguments.

Num a => is a slightly different aspect of the type system. It says that a variable of type a must be an instance of a class of type Num . Common examples of types that are instances of Num include Int and Double . Therefore, Num not a type itself, it is a type class. Num a => represents a restriction on a variable of type a , this is not another argument for the function.

The (+) method is a member of a class of type Num , so you must limit a in such a way as to use (+) . If you try to give f signature a -> a -> a (without restriction), this will not work, because a has no limits, and we don’t know what types it can be. As a result, we could not use (+) on it.

+16
source share

The type of each argument in a function type signature can contain spaces, so most likely a separator without spaces is required, so the compiler (and people!) Can distinguish them.

For example, you can have a parameterized abstract data type:

 data MyType a = MyValue a 

and a function that accepts specific types (built from a constructor of type MyType ):

 myFunc :: MyType Int -> MyType Int -> String 

If you did not have -> between the arguments, the signature will look like

 myFunc :: MyType Int MyType Int -> String -- Not valid code 

and the compiler will have much more trouble developing what the actual arguments of the function should be (and I wonder if this might even be impossible in some cases?). At least this is much less clear.

0
source share

All Articles