Reputation: 678
I have managed to implement a mock filter functions (after lots of attempts)
filter' :: (Num a, Eq a) => (a -> Bool) -> [a] -> [a]
filter' a [] = []
filter' a (x:xs) = if a x
then x : filter' a xs
else filter' a xs
What I don't clearly understand is the type declaration
filter' :: (Num a, Eq a) => (a -> Bool) -> [a] -> [a]
-- filter' (<10) [1,2,3]
-- output = []
We pass in (<10) [1,2,3]
. However in the type declaration (a -> Bool)
we pass in a which comes from the list recursively and the output is either true or false. However what about the expressing the test (<10)?
Why don't we add another Bool?
Upvotes: 2
Views: 118
Reputation: 233477
The type of your filter'
function is only so constrained because you've declared that it is. If you don't declare the type, the compiler will infer a more tolerant type: (a -> Bool) -> [a] -> [a]
. This is the same type as the built-in filter
function:
Prelude> :type filter
filter :: (a -> Bool) -> [a] -> [a]
The expression (< 10)
is a so-called section. It's a partially applied function.
The operator <
is itself a function:
Prelude> :type (<)
(<) :: Ord a => a -> a -> Bool
You can read this as: <
is a function that takes two arguments, both of the generic type a
. The type a
must belong to the typeclass Ord
. When you call <
with such two values, a Bool
value is returned.
Since Haskell functions are curried, you can call a function with only some of the arguments. The return value is a new function that 'waits for the remaining arguments':
Prelude> :type (< 10)
(< 10) :: (Num a, Ord a) => a -> Bool
This type is a little more constrained, because the literal 10
is inferred to belong to the Num
typeclass. Since <
is in use, the original constraint for that (Ord
) is still in effect as well.
The expression [1,2,3]
is is by the compiler inferred as:
Prelude> :type [1,2,3]
[1,2,3] :: Num t => [t]
The values all belong to the Num
typeclass. When you use all of these types together, you get the union of all the inferred types:
Prelude> :type filter (< 10) [1,2,3]
filter (< 10) [1,2,3] :: (Num a, Ord a) => [a]
The Num
typeclass is inferred because of the use of literals, and the Ord
typeclass is inferred because of the use of the <
operator.
Upvotes: 8