Haskell is a purely functional, statically-typed, and lazy (non-strict) programming language. It was created as a result of a collaboration between functional programming researchers in the late 1980s, and its design focused on both theoretical aspects (like category theory and lambda calculus) and practical concerns (like software modularity and code reuse). The language derives its name from Haskell Curry, a mathematician, and a logician known for his work in combinatory logic and for whom the concept of currying is named after.
The main features of Haskell are:
1. **Purely functional**: In Haskell, functions are treated as first-class citizens and have no side effects. This means that functions always return the same output for the same input without altering the state of the system. This approach allows for more predictable and easy-to-debug code.
Example:
-- A purely functional function that adds two numbers
add :: Int -> Int -> Int
add a b = a + b
2. **Statically-typed**: Haskell is statically-typed, meaning that the types of all expressions are known at compile-time. This allows the compiler to catch many types of errors early on and can lead to more reliable code. The type system is also very expressive, including support for parametric polymorphism, type classes, and higher-kinded types.
Example:
-- A generic function that takes a list of any type and returns its length
length :: [a] -> Int
length [] = 0
length (_:xs) = 1 + length xs
3. **Lazy evaluation**: Haskell uses non-strict evaluation, also known as lazy evaluation, which means that expressions are evaluated only when their values are actually needed. This can help improve the performance of certain algorithms and allows for the construction of infinite data structures.
Example:
-- Define an infinite list of natural numbers
naturals :: [Int]
naturals = [0..]
-- Only the first 5 elements of the infinite list are evaluated
take 5 naturals -- returns [0, 1, 2, 3, 4]
4. **Pattern matching**: Haskell supports pattern matching, a form of syntactic sugar that allows for more concise and clear code when dealing with algebraic data types. This simplifies code by allowing the programmer to specify different clauses to handle different cases directly in the function definition.
Example:
-- A simple data type representing a binary tree
data Tree a = Empty | Node a (Tree a) (Tree a)
-- A function that computes the height of the tree using pattern matching
height :: Tree a -> Int
height Empty = 0
height (Node _ l r) = 1 + max (height l) (height r)
5. **Recursion and higher-order functions**: Due to its purely functional nature, Haskell relies heavily on recursion and higher-order functions for iteration and control structures, instead of mutable loops typically found in imperative languages.
Example:
-- A function to compute the factorial of a number using recursion
factorial :: Int -> Int
factorial 0 = 1
factorial n = n * factorial (n - 1)
-- A higher-order function that takes a function and applies it twice
twice :: (a -> a) -> a -> a
twice f x = f (f x)
6. **Strong support for abstraction and code reuse**: Haskell encourages the use of abstract types and functions to promote code reuse and maintainability. Type classes, a major feature in Haskell, provide a way to define functions that can work over a variety of different types, and allow for ad-hoc polymorphism.
Example:
-- Definition of the Eq type class, used for equality testing
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
-- Default definitions for the methods in terms of each other
x == y = not (x /= y)
x /= y = not (x == y)
-- An instance of the Eq type class for the Int type
instance Eq Int where
x == y = intEq x y -- Assuming intEq is a primitive function
In summary, Haskell is a purely functional programming language with a strong static type system, laziness, and features that support abstraction and code reuse. These features make Haskell particularly well-suited for academically-oriented programming, as well as for developing reliable, modular, and maintainable software.