Reputation: 155
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.
Upvotes: 2
Views: 171
Reputation: 476493
insert
is of typet
, it takes two arguments one ofBool
and one of a tuple of two lists of typet
and returns a tuple of two lists of typet
.
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 typet
which returns abool
, and it takes a list of typet
as it's argument and returns a tuple of two lists of typet
.
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])
Upvotes: 10
Reputation: 44830
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])
:
->
s)t
Bool -> ([t],[t]) -> ([t],[t])
Bool
(and Bool
only)([t],[t]) -> ([t],[t])
([t],[t])
(a tuple of two lists, each of which holds values of some type t
)([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:
insert
is this crazy function that returns other functions: type t -> Bool -> ([t],[t]) -> ([t],[t])
insert 2
is of type Bool -> ([t],[t]) -> ([t],[t])
insert 2 True
if of type ([t],[t]) -> ([t],[t])
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.
Upvotes: 4
Reputation: 530823
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.
Upvotes: 5