简体   繁体   中英

Understanding the types of functions

I am trying to understand the types of functions and being able to explain them.

Two functions:

insert :: t -> Bool -> ([t],[t]) -> ([t],[t])
insert a True (b,c) = (a:b,c)
insert a False (b,c) = (b,a:c)

partition :: (t -> Bool) -> [t] -> ([t],[t])
partition p [] = ([],[])
partition p (x : xs) = insert x (p x) (partition p xs)

From my limited knowledge I think that insert function:

  • insert is of type t, it takes two arguments one of bool and one of a tuple of two lists of type t and returns a tuple of two lists of type t.

  • partition is a tuple of type t which returns a bool, and it takes a list of type t as it's argument and returns a tuple of two lists of type t.

Is that the right way of thinking about it or am I getting it wrong? I have been following some tutorials and this is what I've u nderstood so far.

insert is of type t , it takes two arguments one of Bool and one of a tuple of two lists of type t and returns a tuple of two lists of type t .

No . First of all it is important to notice that in Haskell each function takes exactly one parameter. Indeed

insert :: t -> Bool -> ([t],[t]) -> ([t],[t])

is a short and compact form of:

insert :: t -> (Bool -> (([t],[t]) -> ([t],[t])))

in fact the above is still not very verbose, a canonical form would be:

insert :: ((->) t) (((->) Bool) (((->) ((,) ([] t)) ([] t))  ((,) ([] t)) ([] t)))

but the above is of course not very readable, let us thus stick to the second form.

Each function in Haskell takes exactly one parameter. What here happens is that the result of applying a parameter to a certain function, generates a new function.

So if we would generate an expression insert x , we have constructed a function of the type Bool -> (([t], [t]) -> ([t], [t])) .

Informally , one indeed sometimes says that a " function takes n parameters ". But it is important to keep that in mind.

Secondly you forgot about the t . We can informally say that insert takes three parameters, a value of type t , a boolean (type Bool ), and a 2-tuple with two lists of t s. It will return a 2-tuple of two lists of t s. Depending on whether the Bool is True or False it prepends one of the two lists with the given value.

For example:

Prelude> insert 5 False ([], [])
([],[5])
Prelude> insert 5 False ([1,4], [2,5])
([1,4],[5,2,5])
Prelude> insert 5 True ([1,4], [2,5])
([5,1,4],[2,5])
Prelude> insert 3 True ([1,4], [2,5])
([3,1,4],[2,5])
Prelude> insert 3 False ([1,4], [2,5])
([1,4],[3,2,5])

partition is a tuple of type t which returns a bool , and it takes a list of type t as it's argument and returns a tuple of two lists of type t .

No, the parameter here has type (t -> Bool) that is a function . Indeed in Haskell you can pass functions as parameters.

Informally we can say that partition takes a " predicate " (a function that maps values to Bool s) and a list of t s, and it returns a 2-tuple with two lists of t s. Depending on whether the predicate holds for the values in the list, these are sorted in the first or the second list in the 2-tuple.

For example:

Prelude> partition (>3) [1,4,2,5]
([4,5],[1,2])
Prelude> partition (>3) [1,3,0,2]
([],[1,3,0,2])
Prelude> partition (>3) [1,7,8,0]
([7,8],[1,0])
Prelude> partition (>3) [1,7,8,9]
([7,8,9],[1])

No, the types are exactly as shown:

insert has type t -> Bool -> ([t], [t]) -> ([t], [t]) , which means it is a function that takes a value of type t as an argument and returns a function of type Bool -> ([t], [t]) -> ([t], [t]) . Informally, you can think of insert as a function that takes 3 arguments: one of type t , one of type Bool , and one of type ([t], [t]) , and returns another value of type ([t], [t]) .

partition is a function that takes another function (of type t -> Bool ) as its argument, and returns a function of type [t] -> ([t],[t]) . Again informally, you can think of partition as taking two arguments (of type t -> Bool and type [t] ) and returning a value of type ([t], [t]) .

-> itself is a type-level operator; it takes two types as arguments and returns a function type. It is right-associative, which means a -> (b -> c) and a -> b -> c are equivalent.

No, insert is a function , so it can't be "of type t ". If it were of type t , it would be a value :

a :: Int
a = 5

Here a is of type Int .

As you can see from the function implementation, insert takes three arguments:

insert a True (b,c) = ...

The arguments are a , True and (b, c) .

So, the type of insert is exactly t -> Bool -> ([t],[t]) -> ([t],[t]) :

  1. it's a function (because of -> s)
  2. ...of one argument of some type t
  3. ...and returns another function of type Bool -> ([t],[t]) -> ([t],[t])
    1. ...which takes one argument of type Bool (and Bool only)
    2. ...and returns a function ([t],[t]) -> ([t],[t])
    3. (this should be one level of indentation deeper) ...which takes one argument of type ([t],[t]) (a tuple of two lists, each of which holds values of some type t )
    4. ...and returns, finally, a value of type ([t],[t])

Now, this looks like a mess: functions returning other functions that return functions... But this can be simplified. You can think of insert as a function of three arguments:

  1. insert is this crazy function that returns other functions: type t -> Bool -> ([t],[t]) -> ([t],[t])
  2. insert 2 is of type Bool -> ([t],[t]) -> ([t],[t])
  3. insert 2 True if of type ([t],[t]) -> ([t],[t])
  4. insert 2 True ([1], [2]) is of type ([t],[t])

BOOM! The last call actually returned a value, not a function! And due to this, one can treat insert as a function of three arguments. This thing is called currying, and it's named after the same man after whom Haskell is named - Haskell Curry.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM