For a binary operation , its Cartesian lifting sends lists to the list comprehension , with the right generator varying fastest. On finite lists this preserves associativity but need not preserve commutativity or idempotence, because list order and repeated entries matter.
Fair enumeration 2026-10-24
A fair enumeration emits every required occurrence after finitely many computation steps. The inputs can be infinite; completeness does not mean waiting until they have all been exhausted. If two proper lazy lists alternate their next available entries, every occurrence reaches the output after finitely many swaps. In contrast, a nested list comprehension can remain forever in its first infinite inner list. Fairness concerns input occurrences, even when different occurrences happen to have equal values.
List comprehension Created 2026-10-20 Updated 2026-11-05
A list comprehension constructs a list by applying an expression to elements selected through generators and optional predicates. In Python,
[x*x for x in range(5) if x % 2 == 0] gives [0, 4, 16]. The generator sets the traversal, the predicate filters values and the expression transforms those retained. Nested generators preserve an order, so changing them can change output order and termination for infinite inputs in lazy languages. A Python list comprehension constructs the whole result eagerly; a generator expression instead produces values on demand.[ref]Commutativity of the element binary operation does not remove the ordering of a list comprehension. For ordinary addition, take
xs = [1,2] and ys = [10,20]. ThenThe middle entries occur in opposite orders. Henceinvert :: [(a, a)] -> [(a, a)]
invert r = [(y, x) | (x, y) <- r]Thus , the converse relation. Swapping is a bijection, since doing it twice restores the original pair. Therefore distinct input pairs remain distinct; no equality constraint or duplicate removal is necessary.
The answer depends on the actual definitions above, especially the direction used for duplicate removal.
invertworks for an infinite binary relation enumerated by a lazy list: each output needs only one input pair, and swapping preserves distinctness.removedupis productive whenever the input has infinitely many distinct values and equality tests terminate. Each next new value occurs at a finite input position and passes finitely many earlier filters. In particular, it works on the stipulated distinct-element representation of an infinite binary relation. On an infinite list with only finitely many distinct values it emits those values but cannot certify that there is no next new value; for example, demanding the tail after the first result ofremovedup (repeat 1)diverges.relcompdoes not work for arbitrary infinite inputs. Its nested list comprehension can remain forever in the inner generator. Withr = [(i,0) | i <- [0..]]ands = [(0,j) | j <- [0..]], it emits only pairs whose first component is zero, starving every later outer value. Duplicate removal cannot repair this lack of fair enumeration.transclosedoes not work for a general infinite input: even its firstlength scannot finish. It waits for finite saturation before returning any result.
Thus . A fairer enumeration may improve the latter constructions, but would be a different algorithm; ordinary depth-first enumeration does not become fair through lazy evaluation alone.
Collect the matching replacements and distinguish all three possible multiplicities:The list comprehension selects precisely the rules whose left-hand character is
applyRule :: [Rule] -> Char -> String
applyRule rules c =
case [replacement | (key, replacement) <- rules, key == c] of
[] -> error ("no rule for " ++ [c])
[result] -> result
(_:_:_) -> error ("multiple rules for " ++ [c])c. A singleton is the required unique match. The third branch also rejects duplicate rules with identical right-hand sides: there are still two matching rules. Finiteness of the rule list makes the uniqueness test decidable. The returned replacement may itself be empty.Attach zero-based indices before filtering:The zip function pairs each state with its position in the original sequence. The list comprehension retains exactly those pairs satisfying the prefix test, preserving their order and their original indices. The declared result makes
findStates :: [State] -> String -> [(State, Integer)]
findStates states str =
[(state, i) |
(state, i) <- zip states [0..],
isPrefix str state][0..] a stream of Integer indices. An empty search string matches every state, including an empty state, because the empty list is a prefix of every list.Use the source's The outer list comprehension pairs corresponding rows; the inner one pairs corresponding entries. Checking both dimensions before using
type Matrix a = [[a]]. For its nonempty rectangular matrices, the number of rows and the first row's length determine the dimensions:dimensions :: Matrix a -> (Int,Int)
dimensions rows = (length rows, length (head rows))
addMat :: Num a => Matrix a -> Matrix a -> Matrix a
addMat a b
| dimensions a /= dimensions b = error "addMat: incompatible sizes"
| otherwise = [[x + y | (x,y) <- zip r s]
| (r,s) <- zip a b]zip prevents silent truncation. Thus the result has entries , the definition of matrix addition. The Num constraint supplies the addition operation.Reuse For of size and of size , every selected row and column has length . Their dot product isThe two list comprehensions place this entry in row , column of an result. The dimension guard checks exactly the compatibility condition for matrix multiplication. The shared
dimensions and either transpose definition above:multMat :: Num a => Matrix a -> Matrix a -> Matrix a
multMat a b
| colsA /= rowsB = error "multMat: incompatible sizes"
| otherwise = [[sum (zipWith (*) row col) | col <- columnsB]
| row <- a]
where
(_,colsA) = dimensions a
(rowsB,_) = dimensions b
columnsB = transpose bcolumnsB avoids recomputing the transpose for each row; the arithmetic cost is in the unit-cost model.All three Haskell definitions have A list comprehension gives Each version retains the input order and needs no equality or Haskell Ord class constraint on the element type.
filter :: (a -> Bool) -> [a] -> [a]. A recursive list filter isfilter p [] = []
filter p (x:xs)
| p x = x : filter p xs
| otherwise = filter p xsfilter p xs = [x | x <- xs, p x]. A right fold givesfilter p = foldr (\x ys -> if p x then x:ys else ys) []Using only list comprehensions:
type Matrix a = [[a]]
type Grid = Matrix Int
choicesComp :: Grid -> Matrix [Int]
choicesComp rows =
[[[d | d <- [1..9], n == 0 || d == n] | n <- row] | row <- rows]The input contract restricts each cell to zero or a digit from one to nine. A definition using standard list functions is:
choicesMap :: Grid -> Matrix [Int]
choicesMap = map (map choice)
where
choice :: Int -> [Int]
choice 0 = [1..9]
choice n = [n]
choices :: Grid -> Matrix [Int]
choices = choicesMapBoth preserve the row structure and construct the initial constraint domains of the Sudoku problem. The alternative names let both definitions coexist; either can supply
choices.Construct each possible winning line, then test whether one is contained in the player's occupancy set:
winningLines :: [Places]
winningLines =
[[(r,c) | c <- [0..2]] | r <- [0..2]] ++
[[(r,c) | r <- [0..2]] | c <- [0..2]] ++
[[(i,i) | i <- [0..2]], [(i,2-i) | i <- [0..2]]]
won :: Places -> Bool
won ps = any (\line -> all (`elem` ps) line) winningLinesThe list comprehension generates three rows, three columns and two diagonals. Each has exactly three coordinates; therefore
all holds precisely when every coordinate of that line is occupied by this player. Taking any over the eight lines proves the win test correct.The ordinary list comprehension exhausts the inner list before advancing the outer one. Therefore it enumerates every argument pair when
ys is finite, or when xs has at most one element. If ys is infinite and xs has two or more elements, it stays with the first outer element forever and misses every pair with a later outer occurrence. Applying a constant f might hide missing pairs as duplicate values; it does not make the enumeration fair.An empty-result caveat remains when
ys = [] and xs is infinite: the literal comprehension traverses infinitely many empty rows and does not return a terminating empty list. Its occurrence requirement is vacuous, but its productivity as an empty-result computation fails. An explicit empty-input test avoids that particular failure.Use arbitrary-precision integers for positions, avoiding overflow on infinite sequences:The type synonym
type Seq a = a -> [Integer]
rep :: Eq a => [a] -> Seq a
rep xs v = [i | (i,x) <- zip [0..] xs, x == v]Seq a needs no equality constraint; the equality test is required specifically by rep. The list comprehension visits input positions in increasing order, so the output is an increasing list of precisely the matching positions. For finite inputs this includes a terminating empty list when the value is absent. Infinite inputs require the additional productivity distinctions discussed below. This is an occurrence-position representation of a sequence. Standard Prelude Created 2026-10-24 Updated 2026-10-31
The automatically available standard module of Haskell, supplying basic types, classes and operations such as list folds, arithmetic and list comprehension support. A program can explicitly hide an imported name when providing an alternative definition. The reference definitions specify behavior, rather than requiring a particular implementation strategy.[ref]
Haskell 2010 Language Report. Chapter 9, introductory paragraphs and module Prelude exports