Polyglot
Languages Haskell operators
Haskell § operators

Operators

Haskell’s operators are functions — values whose names consist of symbol characters rather than letters. Every operator is a regular function, and every regular function may be used in operator position by surrounding its name with backticks. The duality is one of the language’s distinguishing features; it admits compact higher-order programming and a wealth of user-defined operators in the standard library and beyond. This page covers the operator surface, the precedence and associativity rules, the conventional symbols, and the conventions for using each.

Operators are functions

An operator is a function whose name is a sequence of symbol characters: +, -, *, /, ++, <>, >>=, <$>, >>>, etc. The operator is a value:

1 + 2          -- infix application
(+) 1 2        -- prefix application; (+) names the function

addPair :: (Int, Int) -> Int
addPair (x, y) = (+) x y

increment :: [Int] -> [Int]
increment = map (+ 1)         -- partial application as a section

Conversely, a regular (alphanumeric) function may be used in operator position by enclosing its name in backticks:

10 `div` 3         -- 3
10 `mod` 3         -- 1
elem 5 [1..10]     -- True
5 `elem` [1..10]   -- True; backtick form

The convention is to use the prefix form for alphanumeric functions and the operator form for symbol operators; the backtick form is reserved for cases where the infix reading is more natural (x div y reads better than div x y for a programmer used to mathematical notation).

Sections

A section is a partial application of an infix operator — supplying one operand to produce a function expecting the other:

(+ 1)        -- \x -> x + 1
(1 +)        -- \x -> 1 + x
(* 2)        -- \x -> x * 2
(/ 2)        -- \x -> x / 2
(2 /)        -- \x -> 2 / x       -- the asymmetry matters

(> 0)        -- \x -> x > 0
(`div` 2)    -- \x -> x `div` 2

Sections are the conventional Haskell shorthand for “the function I would have written with a lambda”. The exception is - (subtraction), which is also unary negation; the section (- 1) is parsed as the literal -1, not as \x -> x - 1. Use subtract 1 or (\x -> x - 1) for the partially-applied subtraction.

Arithmetic operators

The principal arithmetic operators:

1 + 2            -- 3
3 - 1            -- 2
2 * 4            -- 8
10 `div` 3       -- 3 (integer division, truncates toward negative infinity)
10 `mod` 3       -- 1 (matches `div`)
10 `quot` 3      -- 3 (integer division, truncates toward zero)
10 `rem` 3       -- 1 (matches `quot`)
10 / 4           -- 2.5 (floating-point division; requires Fractional)
2 ^ 10           -- 1024 (non-negative integer exponent)
2 ^^ (-3)        -- 0.125 (Fractional, integer exponent)
2 ** 0.5         -- 1.414... (Fractional, Floating exponent)

negate 5         -- -5
abs (-5)         -- 5
signum (-3)      -- -1

The four exponentiation operators (^, ^^, **) reflect Haskell’s strict numeric typing:

OperatorBaseExponent
^Num aIntegral b, non-negative
^^Fractional aIntegral b (negative permitted)
**Floating aFloating a

The distinction is the principal source of “why does my exponentiation not type-check” surprises in Haskell.

Division for integers (Int, Integer, Word) uses div/mod or quot/rem (the difference is the rounding for negative dividends). Division for fractional types uses /.

Comparison and equality

The comparison operators require an Ord instance; equality requires Eq:

1 == 2          -- False (Eq)
1 /= 2          -- True  (not equal)
1 < 2           -- True  (Ord)
1 <= 2          -- True
1 > 2           -- False
1 >= 2          -- False

compare 1 2     -- LT
compare 2 1     -- GT
compare 1 1     -- EQ

The compare function returns an Ordering (LT, EQ, GT) and is the foundation for sort, min, max, and similar.

Logical operators

The principal logical operators:

True && False        -- False (short-circuiting)
True || False        -- True  (short-circuiting)
not True             -- False
otherwise            -- = True; convention for the catch-all guard

&& and || short-circuit: if the left operand determines the result, the right is not evaluated.

List operators

[1, 2] ++ [3, 4]     -- [1, 2, 3, 4] (concatenation)
1 : [2, 3]           -- [1, 2, 3] (cons)
[1, 2, 3] !! 1       -- 2 (indexing; partial)
length [1, 2, 3]     -- 3
null []              -- True
elem 2 [1, 2, 3]     -- True
[1, 2, 3] \\ [2]     -- [1, 3] (list difference)

The : operator is the cons — prepending a single element to a list — and is part of the list type’s definition. The ++ is concatenation. Both are O(n) where n is the length of the left operand.

The !! indexer is partial: indexing past the end produces a runtime error. The conventional safe alternative is the Maybe-based lookup for keyed lists or Data.List.lookup.

Function application and composition

Two operators reshape function application:

$ — function application

The $ operator is right-associative function application:

f $ x          -- = f x
f $ x + y      -- = f (x + y)
print $ map (* 2) [1..10]

The principal use is to avoid parentheses around the argument: print $ map (* 2) [1..10] instead of print (map (* 2) [1..10]). The parsing depends on $ having very low precedence (it binds looser than nearly anything else).

. — function composition

The . operator composes functions:

f . g          -- = \x -> f (g x)
negate . abs   -- the function "abs then negate"

(map negate . filter (> 0)) [1, -2, 3]    -- [-1, -3]

Composition is the conventional Haskell mechanism for building functions from smaller pieces. The composition f . g . h $ x reads as “apply h, then g, then f to x”.

The $ and . together admit point-free style — definitions that name no parameters:

sumOfSquares :: [Int] -> Int
sumOfSquares = sum . map (^ 2)

evenCount :: [Int] -> Int
evenCount = length . filter even

The point-free form is concise but can be cryptic; the conventional advice is to use it where it improves readability and to fall back to the explicit form when it does not.

Functor/Applicative/Monad operators

A small set of operators shape the conventional Haskell style:

(<$>) :: Functor f => (a -> b) -> f a -> f b           -- fmap, infix
(<*>) :: Applicative f => f (a -> b) -> f a -> f b      -- applicative apply
(*>)  :: Applicative f => f a -> f b -> f b             -- discard left
(<*)  :: Applicative f => f a -> f b -> f a             -- discard right
(>>=) :: Monad m => m a -> (a -> m b) -> m b             -- monadic bind
(>>)  :: Monad m => m a -> m b -> m b                    -- discard left (sequence)
(=<<) :: Monad m => (a -> m b) -> m a -> m b             -- bind, flipped

These are the principal operators of the Functor / Applicative / Monad type-class hierarchy; they appear throughout Haskell code. The full treatment is in Functors and applicatives and Monads.

User-defined operators

A user may define operators with custom symbols:

infixl 6 +++
(+++) :: [a] -> [a] -> [a]
xs +++ ys = xs ++ ys ++ xs

[1, 2] +++ [3, 4]    -- [1, 2, 3, 4, 1, 2]

The infixl 6 declaration sets the operator’s fixity (associativity and precedence). The choices are:

  • infixl — left-associative (a + b + c = (a + b) + c).
  • infixr — right-associative.
  • infix — non-associative (must be parenthesised).

The precedence is an integer 0-9; higher binds tighter. The standard library’s operators have well-known precedences:

PrecedenceExamples
9., !!
8^, ^^, **
7*, /, div, mod, quot, rem
6+, -
5:, ++
4==, /=, <, <=, >, >=, elem, notElem
3&&
2`
1>>, >>=
0$, seq

User-defined operators conventionally use precedence 4-7 unless there’s a specific reason to differ.

Custom operators in libraries

Several libraries use custom operators heavily. The lens library, for example, uses ^., .~, %~, & for getter, setter, modifier, and the pipe-flip:

import Control.Lens

person ^. name              -- get the name
person & name .~ "Alice"    -- set the name to "Alice"
person & age %~ (+ 1)       -- increment the age

Reading code that uses substantial operator surface requires familiarity with the library’s conventions. The principal advice for new code: prefer named functions for new operators; use operators only when the abbreviation is genuinely useful and the precedence is conventional.

Operator precedence and associativity

The full precedence and associativity table (selected, in order from highest to lowest):

LevelOperatorsAssociativityNotes
9., !!right, leftcomposition; list index
8^, ^^, **rightexponentiation
7*, /, div, mod, quot, remleftmultiplicative
6+, -leftadditive
5:, ++rightlist operations
4==, /=, <, <=, >, >=non-associativecomparison
4<$>, <*>, <|>left, left, leftapplicative; alternative
3&&rightlogical and
2||rightlogical or
1>>, >>=leftmonadic
0$, seq, $!rightapplication; strict

When in doubt, parenthesise. Haskell does not penalise redundant parentheses.

Sequence points and order of evaluation

Haskell is non-strict (lazy) — subexpressions are evaluated only when their values are required. The order of evaluation is therefore not strictly determined by the source order; the compiler is free to evaluate in any order consistent with dependencies.

The principal practical consequences:

  • Operator arguments may not evaluate in source order. f x + g y may evaluate g y before f x.
  • Side effects must be in IO (or another effectful monad) to be ordered. Pure computations have no side effects to order.
  • Forced evaluation (with seq, !, deepseq) controls the strict-evaluation order when needed.

The treatment of evaluation order is in Laziness.

A note on what Haskell does not have

The operators that some other languages provide and Haskell’s status:

Operator / FeatureAvailable?
Assignment (=)No (variables are immutable)
Compound assignment (+=, -=, etc.)No
Increment/decrement (++, --)No (++ is concatenation)
Pointer dereference / address-ofNo
Member access (.)No (the . is composition)
Sizeof / typeofNo directly; Data.Typeable admits some reflection
CastingNo implicit; explicit through type-class methods
Bitwise operatorsThrough Data.Bits (.&., `.
Operator overloading by classNot by class membership; operators are functions, defined per-type via type-class instance
Custom operatorsYes (extensive)

The combination — operators as first-class functions, sections for partial application, the function-composition surface, the absence of mutation — produces a coding style substantially different from C-family languages. The conventions are well-established; the discipline of writing idiomatic Haskell is largely the discipline of using the operator surface fluently.