# Polymorphic types

We study the concept of polymorphic types in Haskell and write polymorphic versions of previously written functions.

## Introduction to polymorphism

So far, all the functions we have written ourselves operated on concrete types. For example, the `containsMatchingInt`

function from the previous lesson operates on functions of type `Int -> Bool`

and lists of type `[Int]`

. It can not process functions or lists of any other type.

```
containsMatchingInt :: (Int -> Bool) -> [Int] -> Bool
containsMatchingInt test [] = False
containsMatchingInt test (x:xs) | test x == True = True
| otherwise = containsMatchingInt test xs
```

The concept of `containsMatching`

, however, is more general. We could write a similar function for lists of doubles, where the predicate also changes to a test on doubles.

```
containsMatchingDouble :: (Double -> Bool) -> [Double] -> Bool
containsMatchingDouble test [] = False
containsMatchingDouble test (x:xs) | test x == True = True
| otherwise = containsMatchingDouble test xs
```

Similarly, one might write `containsMatchingChar`

, `containsMatchingString`

, and so on. But all of these functions use the same general concept of `containsMatching`

. Instead of writing the same logic for each concrete type, we should strive to implement a general `containsMatching`

function that can work on lists of any type.

We know functions that behave like this from the Haskell Prelude. Some examples are the `head`

and `tail`

functions that work on lists of arbitrary element types. These functions are **polymorphic**, which means that they can be applied not only to arguments of just one concrete type, but to a whole range of argument types. In this case, lists of any kind. Let’s see how we can write polymorphic functions ourselves.

Get hands-on with 1200+ tech skills courses.