Parametric polymorphism

Contents
Type variables
forall and free theorems
Write it once
When to specialise

Type variables

A lower case letter in a signature stands for any type. The function must then work for every choice, because it has no way to inspect the value:

identity :: a -> a identity x = x pairUp :: a -> b -> (a, b) pairUp x y = (x, y)

identity cannot add one, print the value, or compare it with anything, because none of those operations exist for an unknown type. That restriction is the feature.

forall and free theorems

The signature means for all a, and you can write the quantifier explicitly. It changes nothing here but makes the promise visible:

identity :: forall a. a -> a identity x = x

A famous consequence is that a function [a] -> [a] can only reorder, drop, or duplicate elements. It cannot invent an a, because it has none. Reading a signature therefore tells you a lot about the implementation before you open it.

Write it once

Generic helpers replace a family of near-identical functions. These three work for strings, lists of numbers, lists of records, and anything else:

firstOr :: a -> [a] -> a firstOr d [] = d firstOr _ (x:_) = x lastOf :: [a] -> Maybe a lastOf [] = Nothing lastOf xs = Just (foldl1 (\_ y -> y) xs)

The default value in firstOr is the price of staying total. Returning Maybe instead, as lastOf does, moves that decision to the caller.

When to specialise

Generality is not free. A function that needs Ord should say so in the constraint rather than pretend to be fully polymorphic, and a hot loop may need a concrete type so GHC can unbox the numbers:

-- too general to sort sortAny :: [a] -> [a] -- honest about what it needs sortAny :: Ord a => [a] -> [a] sortAny = sort

Start as general as the body allows, then add the narrowest constraint that compiles. The signature is documentation the compiler checks.

Next: Maybe and null safety

Article author: Arthur Isaev

Related articles
Haskell
Haskell course: zero to hero in 50 lessons
What is Haskell and why it matters
Install the toolchain with GHCup
GHCi: the interactive shell
Your first program
Values with let and where
Types and type signatures
Functions and application
Currying and partial application
Operators, sections, and fixity
if, guards, and pattern guards
case expressions
Pattern matching
Tuples
Lists
Ranges and enumeration
List comprehensions
Recursion replaces loops
map, filter, and foldr
Lambdas and composition
Type classes
Parametric polymorphism
Maybe and null safety
Either and recoverable errors
data: product types
data: sum types
Records
newtype and type synonyms
Recursive data: binary trees
Functor
Applicative
Monad
do notation
IO and purity
Files and IO
Modules and imports
Cabal projects
Dependencies
Testing with HUnit
Property testing with QuickCheck
Laziness and infinite lists
Strictness and space leaks
Folds
Monoids
Traversable
Monad transformers
Errors and exceptions
Concurrency
Parsing with Megaparsec
Capstone: a todo CLI
Hero roadmap: the whole course on one page

Search this site

Channel @aofeed Chat @aofeedchat

Contacts and cooperation:
I recommend our hosting beget.com
Write to info@aredel.com if you:
1. Want to write an article for our site or translate an article into your native language.
2. Want to place thematically relevant ads on the site.
3. Ads on my site pass maximum censorship. If you see an ad block unsuitable for school-age children, shocking or misleading - please contact us by e-mail
4. Found a mistake, inaccuracy, bug, etc. on the site. ... .......
5. Articles can be shared on social media by clicking a network icon: