Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The difference in Haskell function argument type declaration

Tags:

haskell

I am new to Haskell. I am just confused by the difference between the argument type declarations here:

someFunction :: (Integral a) => a -> Bool -> a 

This will compile.

someFunction :: (Integral a, Bool b) => a -> b -> a 

Yet given this, the compiler will complain:

• Expecting one fewer arguments to ‘Bool’
  Expected kind ‘* -> Constraint’, but ‘Bool’ has kind ‘*’
• In the type signature:
    someFunction :: (Integral a, Bool b) => a -> b -> a

What is the difference between the two declarations? I tried to search with Google but it seems there is no direct answer to this one.

Thanks!


Thanks for the quick answers guys. And they lead me to the next question: what are the thoughts behind having argument constraints to be in different places in the declaration? (vs C-like languages where the constraints, abstract or concrete, are mixed together in the declaration)

like image 233
Chen Sturmweizen Avatar asked Dec 17 '17 10:12

Chen Sturmweizen


2 Answers

Integral is not a type: it is a typeclass. It describes a constraint on a type.

So, the signature:

someFunction :: (Integral a) => a -> Bool -> a 

means that "As long as a is an instance of Integral, this function can work on it".

Your second example of (Integral a, Bool b) => a -> b -> a does not make sense, because Bool is a type, not a typeclass.

What does it mean to be an instance of Integral? Well, it means that somewhere, someone has written, for some type, for instance Int:

instance Integral Int where
    -- (declarations here)

Here's an example of your own typeclass: Sizeable:

class Sizeable s where
    size :: s -> Int

We can see this as types which have an associated size, which is defined by the size function. So, we can add some instances:

instance Sizeable [a] where
    size = length

instance Sizeable (a,b) where
    size _ = 2

instance Sizeable (a,b,c) where
    size _ = 3

So, we could write the function:

sizePlusOne s = (size s) + 1

And this would have the type:

sizePlusOne :: (Sizeable s) => s -> Int
like image 163
AJF Avatar answered Dec 07 '22 23:12

AJF


The items on the left of the => are type constraints. Haskell uses type classes for it. A type class is best seen as an interface in programming languages like Java, etc.

For instance in Haskell there is a class Eq:

class Eq a where
  (==) :: a -> a -> Bool
  (/=) :: a -> a -> Bool

Now we can make a type Int a "member" of the Num typeclass, by declaring it an instance of Num, and **implement the functions that are part of the class. Say for instance we want to make the (positive) Peano numbers an instance of Eq, we then first define the Peano numbers as:

data Peano = Zero | Succ Peano

and now we can make it an instance of Eq:

instance Eq Peano where
    Zero == Zero = True
    s(x) == s(y) = x == y
    _ == _ = False

    Zero /= Zero = False
    s(x) /= s(y) = x /= y
    _ /= _ = True

So now we can compare two Peano instances with ==, and Peano is registered as an instance of Eq. So that means that if we write something like:

someFunction :: Eq a => a -> a -> a -> Bool
someFunction x y z = x == y && y == z

then a can be Peano, since we require that Eq Peano (on the left side) holds.

Type classes are thus used to allow you to use abstract functions (like for instance == and /=), without knowing how these are defined for the concrete type.

Bool on the other hand is not a typeclass, it is a concrete type. So you can not write Bool b. It also makes no sense syntactically, since a type class requires a parameter (i.e. Eq Peano), whereas Bool is a type with no type parameter).

like image 21
Willem Van Onsem Avatar answered Dec 08 '22 00:12

Willem Van Onsem