What general tips do you have for golfing in Haskell? I am looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to Haskell. Please post only one tip per answer.

If you are new to golfing in Haskell, please have a look at the Guide to Golfing Rules in Haskell. There is also a dedicated Haskell chat room: Of Monads and Men.

  • 1
    \$\begingroup\$ Seeing the number of answers till now, I am in doubt about whether Haskell is even a good language for code golfing or not? \$\endgroup\$ Commented Jan 24, 2014 at 10:23
  • 11
    \$\begingroup\$ Why only one tip per answer? Also, every language is a good language for golfing. Just don't always expect to win. \$\endgroup\$
    – unclemeat
    Commented Feb 12, 2014 at 0:46
  • 11
    \$\begingroup\$ @unclemeat This way people could upvote the good ones to the top without upvoting the bad ones only because they were written by the same guy in the same answer. \$\endgroup\$ Commented Jun 1, 2014 at 12:10
  • 3
    \$\begingroup\$ Special request, String compression. \$\endgroup\$
    – J Atkin
    Commented Feb 20, 2016 at 0:07
  • 1
    \$\begingroup\$ This is probably not suited as an anwer, but I'm still want to add it here: wiki.haskell.org/Prime_numbers_miscellaneous#One-liners \$\endgroup\$
    – flawr
    Commented May 25, 2016 at 19:38

58 Answers 58


Shorter transpose

To use the transpose function Data.List has to be imported. If this is the only function needing the import, one can save a byte using the following foldr definition of transpose:

import Data.List;transpose

Note that the behaviour is only identical for a list of lists with the same length.

I successfully used this here.

  • 1
    \$\begingroup\$ If you know the exact size of the list map((<$>[0..k]).(!!)) where k is the size will likely be shorter. \$\endgroup\$
    – Wheat Wizard
    Commented Sep 1, 2021 at 17:38
  • \$\begingroup\$ @WheatWizard As written, map((<$>[0..k]).(!!))s computes the identy of a list of lists s. The approach works if we switch s and [0..k], but then (!!) needs to be flipped. I guess simply [map(!!i)s|i<-[0..k]] then beats the point-free approaches. Try it online! \$\endgroup\$
    – Laikoni
    Commented Oct 13, 2023 at 8:03

Replace let by lambda

This can usually shorten a lone auxiliary definition that can't be bound with a guard or defined globally for some reason. For example, replace

let c=foo a in bar

by the 3 bytes shorter

(\c->bar)$foo a

For multiple auxiliary definitions, the gain is probably smaller, depending on the number of definitions.

let{c=foo a;n=bar a}in baz
(\c n->baz)(foo a)$bar a

let{c=foo a;n=bar a;m=baz a}in qux
(\c n m->qux)(foo a)(bar a)$baz a

let{c=foo a;n=bar a;m=baz a;l=qux a}in quux
(\c n m l->quux)(foo a)(bar a)(baz a)$qux a

If some of the definitions refer to the others, it is even harder to save bytes this way:

let{c=foo a;n=bar c}in baz
(\c->(\n->baz)$bar c)$foo a

The main caveat with this is that let allows you to define polymorphic variables, but lambdas do not, as noted by @ChristianSievers. For example,

let f=length in(f["True"],f[True])

results in (1,1), but


gives a type error.

  • 1
    \$\begingroup\$ It rarely matters, but "semantically equivalent" promises a bit too much. We have polymorpic let, so we can do let f=id in (f 0,f True). If we try to rewrite this with lambda it doesn't type check. \$\endgroup\$ Commented Jan 17, 2017 at 21:09
  • \$\begingroup\$ @ChristianSievers That's true, thanks for the note. I edited it in. \$\endgroup\$
    – Zgarb
    Commented Jan 18, 2017 at 8:13

Avoid sorting

Haskell doesn't have a built-in sorting in Prelude. It can be accessed with an import:

import Data.List

This also gives you access to functions like sortOn and sortBy, as well as other functions in Data.List. But, many times you don't need the full power of sorting and can make do without an import.

Check if a list is sorted

scanl1 max l==l

This requires the list to be nonempty. As a pointfree predicate:

(==)=<<scanl1 max

You can likewise check if a list is in descending order with scanr1 max l==l.

Sort values lying in a known range

If you know the l contains only, say, digits 0 to 9, you can sort it as:


This allows for duplicates in l. If you know l doesn't have duplicates, or are fine dropping them, you can do:

[d|d<-[0..9],elem d l]



These also works if l is a string, enumerating characters as ['\0'..] or [' '..]. The range of characters is very large but bounded.

Min and max

If you just need the smallest or largest element, minimum or maximum will do. You can also check whether n>=maximum l as all(<=n)l, and similarly for minimum.

Sorted index

One use of sorting is to find the index of some value n in sorted order within l. You can instead do that by counting smaller elements:


If x appears more than once, this gives the index of its first appearance in sorted order.


Online Tools

  • Try it online!
    TIO supports online compilation of Haskell code with the GHC 8.0.2 compiler. As TIO is developed with code golfing in mind it's not just an online interpreter but also offers features like byte count, header and footer sections that do not count towards the total byte count (put your main and test code there), automatic markdown generation for a code golf submission, and more.

  • pointfree.io
    Converts Haskell code to pointfree Haskell code which sometimes is shorter, see this tip.
    Note: When dealing with functions that take two or more arguments, the pointfree version generated by pointfree.io often includes the ap function which is not in Prelude. However <*> is an equivalent inline version of ap and contained in Prelude in ghc 7.10 or higher. The same goes for liftM2, which is also only available when importing Control.Monad. The pointfree version of some expression like \x -> f (g x) (h x) is given as liftM2 f g h, though this can equivalently be expressed as f.g<*>h.

  • pointfree.pages.dev is an alternative site for pointfree.io (which is down at the time of editing).

  • Hoogle

Hoogle is a Haskell API search engine, which allows you to search many standard Haskell libraries by either function name, or by approximate type signature.

Useful to quickly search the documentation or lookup in which packages a function is included.

  • \$\begingroup\$ Note that TIO currently uses GHC 7.8, so the stuff in this tip is not available. \$\endgroup\$
    – Zgarb
    Commented Jan 18, 2017 at 14:05
  • \$\begingroup\$ @Zgarb I asked Dennis if ghc can be updated. Currently one can add import Control.Applicative and/or import Control.Monad into the header on TIO to use <$> ect. \$\endgroup\$
    – Laikoni
    Commented Jan 18, 2017 at 15:18
  • 1
    \$\begingroup\$ @Zgarb TIO's ghc was updated to 8.0.2 thanks to Dennis. \$\endgroup\$
    – Laikoni
    Commented Jan 22, 2017 at 20:06
  • \$\begingroup\$ TIOv2 Alpha was replaced with a fork of TIO Nexus, so GHC8.0.2 should be supported. \$\endgroup\$ Commented Jun 1, 2017 at 15:38

When writing expressions that don't use variables, you don't need to include the function name for scoring purpose. For example:


If f is the golfing answer, the byte count is 7 (you can omit f=). It is possible to write it properly with TIO using CPP.

In the header, include:


Example here.

  • 3
    \$\begingroup\$ Golfier: Add a -cpp compiler flag. \$\endgroup\$
    – Laikoni
    Commented Oct 31, 2017 at 21:22

(Ab)use the Reader Monad/Applicative

There already are comments on pointfree code and one mentioning pure, but I figured I'd make a specific tip for this since I see it a lot.

The Applicative functions in Prelude by default when specialized to functions are as follows

instance Applicative ((->) a) where
    pure = const
    (f <*> g) x = f x (g x)

Note that for this particular type, (=<<) acts almost the same as (<*>), except it takes its second argument flipped (thanks @ØrjanJohansen).

pure is one character shorter than const, as mentioned elsewhere. The especially useful combinator, however, is (<*>). Below are examples of where I've used it recently.

Unique characters

The straightforward way to solve this is

import Data.List
map(\x->(x!!0,length x)).group.sort

But we're here for codegolf, not readable code.

import Data.List

A contrived function

This comes from a function my coworker wrote that I (unashamedly) golfed until it was unreadable.

Suppose we have

data Ex a b c
  = Ex
    { foo :: [(a,b)]
    , bar :: c}

and we want the natural implementation of a function

f :: Ex a b c -> [(a,c)]

I don't have the original solution (it did involve (&&&) from Control.Arrow, though), but the golfed one using (<*>) is


which also abuses the Functor instance of (,) a.

digitSum(n) + n

This golf is part of this answer.

This is a pretty well-golfed function

\n->n+sum[read[d]|d<-show n]

but if we do away with readability and bring in (<*>), we can shave off a character:

-- = \x -> foldr ((+) . read . pure) x (show x)

do notation

If your function is of the form \x -> (expr) x and (expr) contains some repeated blah x often, you can sometimes save bytes with Reader monad do notation and b<-blah.

Here are some strange examples:

\x->max(div x 3*div x 5^div x 7)x   -- original expression
f x|d<-div x=max(d 3*d 5^d 7)x;f    -- define f=... then use f
\x->max(x!3*x!5^x^7)x;(!)=div       -- define (!)=div
do d<-div;max$d 3*d 5^d 7           -- aha!

\x->product x:filter(/=product x)x      -- original expression
(:)<$>product<*>(filter=<<(/=).product) -- yeah, no...
f x|p<-product x=p:filter(/=p)x;f       -- define f=... then use f
\x->p x:filter(/=p x)x;p=product        -- define p=product
do p<-product;(p:).filter(/=p)          -- aha!

In general, do a<-b;c<-d;e is equivalent to \x->let{a=b x;c=d x}in e x.

  • \$\begingroup\$ Using =<< instead of <*> gives a different parameter order that is sometimes useful. \$\endgroup\$ Commented Jul 24, 2019 at 10:14
  • 2
    \$\begingroup\$ (<*>) is the S combinator; pure is the K combinator. \$\endgroup\$ Commented Jul 24, 2019 at 19:52

The function 0^x

This maps positive integers to 0 and zero to 1. So, it acts a little bit like !x in C-like languages.

It crashes for negative x; in this case you can write 0^x^2, which parses as 0^(x^2).

This is not technically specific to Haskell, but Haskell is quite strict about converting between numbers and booleans compared to some other common code golf languages. It's often shorter to use 0^x and stay in Integer-land than to check x==0.

Even expressions like 0^(x-42)^2 come in handy sometimes (if x==42 then 1 else 0).


if x==0 then 'B' else 'A'
"AB"!!(0^x)       -- if x is never negative
  • \$\begingroup\$ If you need to use the arbitrary precision Integer type, it appears that 0^x can also convert x from Integer to Int for use with (!!) \$\endgroup\$
    – SirBogman
    Commented Nov 27, 2021 at 3:51

Partition a string with mapM and words

This function computes all partitions of a given string into nonempty contiguous substrings:

map(words.concat).mapM(\c->[[c],c:" "])

The idea is that the mapM non-deteministically replaces each character c with either "c" or "c ", and the resulting lists are concatenated and split at spaces. There are two gotchas: the string must not contain spaces (if it contains spaces but not line breaks, use "\n" and lines for one extra byte), and each partition occurs twice in the resulting list (with and without a trailing space, which gets eaten by words).

I've used this technique a couple of times (at least here, here and here). It's pretty flexible, since you can apply more functions after words to modify the partitions, and/or replace map with another iteration function, like any.

  • 1
    \$\begingroup\$ Great idea using string function to split! I'll have to use this sometime. The best general-purpose alternative I found was 45 bytes, 6 longer: foldr(\h t->map([h]:)t++[(h:y):z|y:z<-t])[[]]. \$\endgroup\$
    – xnor
    Commented Nov 4, 2016 at 7:02
  • \$\begingroup\$ When applied directly to some x this can be slightly shortened to words.concat<$>mapM(\c->[[c],c:" "])x . \$\endgroup\$
    – Laikoni
    Commented Apr 24, 2017 at 20:44

Use fromEnum instead of ord

fromEnum is already available in Prelude, while ord needs to be imported from Data.Char. This will save you 12 bytes. With subsequent usages you should define an alias like f=fromEnum and use f.

  • 1
    \$\begingroup\$ If you use it more than once, you should define an alias with e=fromEnum, which is shorter than import Data.Char and costs 1 byte to use. \$\endgroup\$
    – Zgarb
    Commented Jan 11, 2017 at 7:59
  • \$\begingroup\$ @Zgarb: of course, I'm always doing that when writing answers, but for this tip I totally forgot about it. \$\endgroup\$
    – Renzeee
    Commented Jan 11, 2017 at 8:15
  • 1
    \$\begingroup\$ The same goes for chr and toEnum, however the type of toEnum needs to be forced from context. \$\endgroup\$
    – Laikoni
    Commented Jan 13, 2017 at 16:14

Converting to Pointfree

As @Laikoni mentioned in this post, you can use pointfree.io to convert your code to pointfree code. However, if you want to do this conversion manually you can check out this answer on stackoverflow by @luqui, which provides an algorithm:

You need the following combinators, which are in the standard library. I have also given their standard names from combinator calculus.

id :: a -> a                                   -- I
const :: a -> b -> a                           -- K
(.) :: (b -> c) -> (a -> b) -> (a -> c)        -- B
flip :: (a -> b -> c) -> (b -> a -> c)         -- C
(<*>) :: (a -> b -> c) -> (a -> b) -> (a -> c) -- S

Work with one parameter at a time. Move parameters on the left to lambdas on the right, e.g.

f x y = Z


f = \x -> \y -> Z

I like to do this one argument at a time rather than all at once, it just looks cleaner.

Then eliminate the lambda you just created according to the following rules. I will use lowercase letters for literal variables, uppercase letters to denote more complex expressions.

  1. If you have \x -> x, replace with id
  2. If you have \x -> A, where A is any expression in which x does not occur, replace with const A
  3. If you have \x -> A x, where x does not occur in A, replace with A. This is known as "eta contraction".
  4. If you have \x -> A B, then
    1. If x occurs in both A and B, replace with (\x -> A) <*> (\x -> B).
    2. If x occurs in just A, replace with flip (\x -> A) B
    3. If x occurs in just B, replace with A . (\x -> B),
    4. If x does not occur in either A or B, well, there's another rule we should have used already.

And then work inward, eliminating the lambdas that you created.

Sometimes a shortcut with >>= is possible:

  1. If you have \x -> A B x, where x does not occur in A, replace with (\x -> B) >>= A.
  • \$\begingroup\$ in the general case, (\x -> A B x) = join ((\x -> A) <*> (\x -> B)). \$\endgroup\$
    – Will Ness
    Commented Jan 30, 2018 at 18:35
  • \$\begingroup\$ @Razetime thanks for the suggestion, it already is mentioned:) \$\endgroup\$
    – flawr
    Commented Oct 1, 2022 at 14:04

Cheap divisors

The usual way to get the divisors (factors) of n is:

[d|d<-[1..n],mod n d<1]

For example, n=6 gives [1,2,3,6]. But, if you're OK with divisors repeating out of order, you can just do:

gcd n<$>[1..n]

For n=6, this gives [1,2,3,2,1,6]. The code is effectively [gcd k n|k<-[1..n]], takes the greatest common divisor of k with n. These are by definition divisors of n, and each divisor d appears at least once as gcd d n. So, the "set" of divisors is correct. The first appearances are in increasing order, so removing duplicates will match the original code's result.


Define local variables in list comprehensions using singleton lists

For example, rather than writing

[x+y|x<-[1..5],let y=2]

one can write


for a byte less.

This works in do blocks as well, but only in the list context since the [] syntactic sugar is necessary to come out ahead. (I guess any custom monads with single-letter constructors would benefit from this, too, but I don't think I've ever seen any of those show up in code golf.)

  • \$\begingroup\$ @Brute_Force: I meant only to show an example of how this tip would be used; I did not mean to claim that this particular example is realistic. For a more realistic example, consider \a b->[[r..s]|r<-[a..b],s<-[2*r+7],s>7] \$\endgroup\$ Commented Jul 6, 2017 at 23:15


The "canonical" way of computing the powerset of a list is Data.List.subsequences. If it is used only once and applied to a value (as opposed to, say, being fed to map), it's slightly shorter to implement it yourself. Compare:

import Data.List;subsequences x

This saves 0-3 bytes depending on whether you need a space between subsequences and the argument, and whether the latter expression needs parentheses around it. If x has the form f<$>y, you can move f inside the lambda to save some more bytes, particularly if f is also defined by a lambda:

import Data.List;subsequences$f<$>y
concat<$>mapM(\a->[[f a],[]])y

import Data.List;subsequences$(\a->a*3+1)<$>y

It's also possible to compose concat with another function, if you intended to map it over the subsequences anyway:

import Data.List;g<$>subsequences x

If you have already imported Control.Monad, you can implement the powerset function in 18-20 bytes with one of


The last two variants need another function f :: a -> b to be defined, where [a] is the type of the list (doesn't matter what f does or what b is). The optimal choice depends on whether such an f is available, and whether you need parentheses around the expression.

  • 1
    \$\begingroup\$ Might note that this gives 3 different variations in the ordering of the resulting power set. subsequences uses the order that generalizes to an infinite list, although then it's not really a power set. \$\endgroup\$ Commented Jan 11, 2018 at 19:26
  • \$\begingroup\$ Maybe it has some use in some situation (\a->[[a],[]]) has the same length as the point free ((:[[]]).pure). \$\endgroup\$
    – flawr
    Commented Jun 4, 2021 at 21:11

Use Data.Lists

This package defines a lot of nice functions on lists! It’s like Data.List in base, but fancier.

Importing it costs 18 bytes (import Data.Lists\n).

Here are some nice things it exports, on top of everything from Data.List:

Various shortcuts:

  for              ≡ flip map
  unionOf          ≡ foldr union []
  hasAny e x       ≡ any (`elem` e) x
  countElem i      ≡ length . filter (== i)
  list b f xs      ≡ if null xs then b else f xs
  firstOr x        ≡ list x head
  maxList xs       ≡ list 0 maximum
  catchNull f      ≡ list Nothing (Just . f)
  lastToMaybe      ≡ catchNull last
  chop f           ≡ list [] (\x->let (y,ys)=f x in y:chop f ys)
  pair x y         ≡ guard (length x == length y) >> Just (zip x y)
  pairWith f x y   ≡ guard (length x == length y) >> Just (zipWith f x y)

Split functions:

  splitOn "x" "axbxc"                  ≡ ["a","b","c"]
  endBy ";" "foo;bar;baz;"             ≡ ["foo","bar","baz"]
  splitWhen (<0) [1,3,-4,5,7,-9,0,2]   ≡ [[1,3],[5,7],[0,2]]
  splitOneOf ";.," "foo,bar;baz.gluk"  ≡ ["foo","bar","baz","gluk"]
  endByOneOf ";.," "ae;io.,u,"         ≡ ["ae","io","","u"]
  chunk 3 ['a'..'k']                   ≡ ["abc","def","ghi","jk"]
  replace old new                      ≡ intercalate new . splitOn old

Variants of Data.List functions:

  elemRIndex     ∷ a -> [a] -> Maybe Int   (Rightmost index)
  powerslice     ∷ [a] → [[a]]             (All slices of a list)

  spanList       ∷ ([a] → Bool) → [a] → ([a], [a])
  breakList      ∷ ([a] → Bool) → [a] → ([a], [a])
  takeWhileList  ∷ ([a] → Bool) → [a] → [a]
  dropWhileList  ∷ ([a] → Bool) → [a] → [a]


  argmin,         argmax           ∷ Ord b ⇒ (a → b) → [a] →   a
  argmins,        argmaxes         ∷ Ord b ⇒ (a → b) → [a] →  [a]
  argminWithMin,  argmaxWithMax    ∷ Ord b ⇒ (a → b) → [a] → ( a,  b)
  argminsWithMin, argmaxesWithMax  ∷ Ord b ⇒ (a → b) → [a] → ([a], b)

Association list functions: treat [(k, v)] as a pseudo-map type.

  delFromAL l k  ≡ filter ((/= k) . fst) l
  addToAL l k v  ≡ (k, v) : delFromAL l k
  keysAL         ≡ map fst
  hasKeyAL k     ≡ any ((== k) . fst)
  flipAL         ∷ [(k, v)] → [(v, [k])]

Integral to Fractional (e.g. Int to Double)

Instead of using the famous

fromIntegral :: (Integral a, Num b) => a -> b

in many cases

realToFrac :: (Real a, Fractional b) => a -> b

works too. and is two bytes shorter.

  • 6
    \$\begingroup\$ There are cases where read.show works too, if GHC can infer the types. (For example: sin$read$show someInt.) \$\endgroup\$
    – lynn
    Commented Nov 18, 2017 at 14:23
  • 1
    \$\begingroup\$ toEnum :: Enum a => Int -> a also works, though slightly more constrained. \$\endgroup\$
    – Bubbler
    Commented Jan 18, 2019 at 7:20

Helper functions

This is (or will) be a list of generic helper functions that occasionally have to be implemented as part of a bigger solution.

  • Primality test: p :: Int -> Bool

    p n=mod(product[1..n-1]^2)n>0
  • Binary to decimal: b :: String -> Int

    b=foldl(\a x->2*a+read[x])0
  • Decimal to binary: d :: Int -> String

    d n=mapM(\_->"01")[1..n]!!n

More to come.

  • 5
    \$\begingroup\$ It might be worth adding two more primality tests: p n=[1|0<-mod n<$>[2..n]]==[1] and p n=all((>0).mod n)[2..n-1]. The former identifies 0 as not prime, the latter is only valid for n>=2. Both can come in handy \$\endgroup\$
    – H.PWiz
    Commented Jan 5, 2018 at 18:03
  • 2
    \$\begingroup\$ Also the ^2 in the primality test is only needed for n==4. \$\endgroup\$ Commented Jan 5, 2018 at 18:37
  • 3
    \$\begingroup\$ The decimal to binary can be d n=mapM("01"<$d)[1..n]!!n, although that depends on defining it as a function, or having another one-letter Int->something function to put with the <$. \$\endgroup\$ Commented Jan 5, 2018 at 18:40
  • \$\begingroup\$ @ØrjanJohansen, some of those tests look similar to Wilson's theorem, but not the same. Where's the square come from? \$\endgroup\$
    – dfeuer
    Commented Mar 14, 2019 at 20:46
  • \$\begingroup\$ @dfeuer It's the shortest way to handle the fact that Wilson's product for the number n=4 is 2 (mod n), instead of 0 (mod n) like for all other composites. \$\endgroup\$ Commented Mar 14, 2019 at 22:36

Haskell + plumbers

Use the plumbers package for a large collection of infix combinators.

For example,

-- Ungolfed
f (x, y) (z, t) = (x + z, y + t)
-- Golfed (without plumbers)
-- Golfed (with plumbers)

Or, let's say we wanted to find the dot product of a list of vectors in the form [(Double, Double)].

-- Ungolfed
g = sum . map (\(x,y) -> x * y)
-- Golfed (without plumbers)
-- Golfed (with plumbers)

Plumbers are a great way to pointfree an expression, especially in the middle of a map or a fold.

As a quick note, the way to read these operators is as follows.


Every plumber takes two functions as arguments. The first character specifies how to combine the results of the two functions. A * means to return a tuple, and a $ means to pass the result of the second function to the first function. The remaining characters specify what to do with the remaining arguments.

< Pass the argument to the left but not the right function
> Pass the argument to the right but not the left function
& Pass the argument to both functions
^ Ignore the argument completely
* The argument is a tuple; split it and pass the parts to the left and right functions
  • 4
    \$\begingroup\$ I like how plumbers calls itself pointless plumbing combinators. \$\endgroup\$
    – Dennis
    Commented Dec 30, 2017 at 18:56

Use pure to build singletons

  • pure is one byte shorter than (:[]) or Right (thanks, Ørjan Johansen).
  • pure.f is one byte shorter than Just .f.
  • pure is shorter than singleton if you need Data.Sequence for some reason.
  • As Ørjan Johansen points out, this works for the writer monad: pure a=(mempty,a). This may occasionally be useful for something like (Nothing, a).
  • \$\begingroup\$ (,)mempty popped into my mind as another example... not sure how useful. \$\endgroup\$ Commented Mar 14, 2019 at 23:46
  • \$\begingroup\$ Oh, and Right too. \$\endgroup\$ Commented Mar 15, 2019 at 3:43

Binary op on two-element list

You can apply a binary operation to a two-element list like:


to get 5-3, or 2. Note that this operator must output the same type as the input, that is be a -> a -> a. So, foldr1(==)[5,3] doesn't typecheck but foldr1 mod[5,3] does.

You can think of this as a list analogue to uncurry(-)(5,3). It may be useful if you take input as a two-element list or one is produced in an intermediate computation, and it would cost extra bytes to match it to [a,b] to extract the values.

Using foldl1 gives the same result as foldr1. The best way to flip the order of the operands seems to just be to flip the operator.


iterate isn't very good

Defining your own function that returns an infinite list is usually shorter. Compare:

f x=x*x+x;iterate f a
g x=x:g(x*x+x);g a

Exceptions include when f is a builtin or operator section, but remember:

  • iterate pred a is just [a,pred a..]
  • iterate(+k)a is just [a,a+k..]

Sometimes I see people write very "pipeline-y" expressions like:


But this can be golfed down all the way to:


Fixity declarations are your friends

As we know it is often cheaper to define an operator instead of a function (see this tip), sometimes it pays off to declare a fixity different from the default (infixl 9). Imagine for example the following which is fairly common (57 bytes):


In that particular case using a fixity declaration is still 1 byte more expensive (58 bytes):

infix 4!

As you can see you can use the low fixity to omit several parentheses, if somewhere in your code you have other parentheses that are due to the high default fixity, you can save bytes for these by declaring another fixity.

Note: This might seem like an arbitrary example but it is realistic as you can see in this answer.


Use pure instead of const. I saw this in someone else's answer, but I don't know where. Of course, it's often better to use a lambda if it's applied.

There's an occasional one byte shorter trick than the lambda \_->x: Use x<$f where f is any one-letter function that just happens to have the same input type as what you want. Note that this never evaluates f itself at all.



In the spirit of saving characters using infix operators, you could save a couple by replacing something like:

map show [1..9]



Use zip

Often you need to map over a list and apply some function which depends on the index of the argument in the list. while a lot of impure languages who have some sort of map builtin have the index be an optional argument, this is impossible in Haskell. instead, use:

mapWithIndex f xs === f<$>zip[0..]xs
                  === [f i x|(i,x)<-zip[0..]xs] {- inlinable version -}
                  === zipWith f[0..]xs
mapWithIndex f    === (f<$>).zip[0..]           {- points free version -}

(This also gives us 1-based indexing for free!)

Often this combines well within list comprehensions, where even a builtin mapWithIndex won't help:

    [ ... | ..., (i,x)<-zip[0..]xs, ...]

Other times, you really want to use the nonexistant equivalent maximumOn of sortOn, but the import is too many bytes, or using maximumBy is too many bytes too. instead, use*:

sortOn f xs === snd$sort$(f>>=(,))<$>xs
            === snd$sort[(f x,x)|x<-xs]   {- inlinable version -}
sortOn f    === snd.sort.(f>>=(,)<$>)     {- points free version -}

Note that sometimes you will need both the best x and its f x, in which case you can get rid of three bytes and have it computed for you for free!

many other uses for this combination are possible too.

  • 1
    \$\begingroup\$ I don't think f<$>zip[0..]xs works as [f i x|(i,x)<-zip[0..]xs]. You'd need uncurry f<$>zip[0..]xs, since f is being given tuples of (i,x). It would work to do zipWith f[0..]xs, \$\endgroup\$
    – xnor
    Commented Mar 27, 2016 at 23:16
  • \$\begingroup\$ @xnor as the code golfer, you can modify your f whatever way fits best. We're not writing library functions here. \$\endgroup\$ Commented Mar 27, 2016 at 23:19
  • \$\begingroup\$ @xnor Oh, that's a way I haven't thought of! I'll add it. \$\endgroup\$ Commented Mar 27, 2016 at 23:20
  • 1
    \$\begingroup\$ Oh, you're saying that f is the outer function that you're defining in the code golf challenge, and so you can choose whether it's curried or takes a tuple? That definitely works for that case, but it wasn't clear from reading it that it wasn't meant to apply to functions you define as as subpart of your golf. \$\endgroup\$
    – xnor
    Commented Mar 27, 2016 at 23:22
  • \$\begingroup\$ @xnor even if f isn't a function you defined yourself, then the fact that both options are possible is useful, because using curry or uncurry will probably be longer than just switching to one of the other options presented here. \$\endgroup\$ Commented Mar 27, 2016 at 23:26

Use empty let instead of otherwise/True

x&y|x=y|let=x  -- Boolean and

While it is no shorter than using 1>0 and usually it makes more sense to use the guard to bind a variable it can be useful when there are source restrictions or the prelude is not in scope.

This works because let statements in guards behave as always true and an empty let is valid.

Empty let also works in list comprehensions, but I believe it completely useless for golfing in that position as it can be omitted.


Pattern match inside list comprehensions

Suppose you have code that looks like (imports for purpose of demonstration)

import Data.List (lookup)
import Data.Maybe (catMaybes)

ys = [(1,"a"), (3, "b"), (5, "c"), (7, "d")]
catMaybes [lookup x ys | x <- [1..10]]

You can change this to

[y | x <- [1..10], Just y <-[lookup x ys]]

I found it particularly helpful when I was pattern matching in a function definition

f (x:xs) = [foo x x'| x' <-[1..10]]
f [] = []

when I could instead put the pattern matching logic inside the list comprehension.

f lst = [foo x x' | x:xs<-[lst], x'<-[1..10]]

This works because incomplete pattern matches are desuarged to mfail instead of error in do notation, so for things like the list monad, we get the empty list. It can be used with do notation for other monads, too, although pure is probably the shortest alternative to [].

f :: [Int] -> Maybe String
f lst = do
  1:xs <- pure lst
  pure "one"
  • 1
    \$\begingroup\$ Some notes: (1) lookup itself can be replaced by pattern matching if you know there cannot be more than one match. (2) You don't need parentheses around patterns before <-. (3) You can shorten [foo x x' | (x:xs)<-[lst], x'<-[1..10]] further to [foo x|x:xs<-[lst]]<*>[1..10]. \$\endgroup\$ Commented Jun 29, 2019 at 1:57
  • \$\begingroup\$ @ØrjanJohansen nice catch on the parentheses, and yeah I’m sure there are other tricks, these were contrived examples to demonstrate the utility. \$\endgroup\$
    – cole
    Commented Jun 29, 2019 at 2:23

Endless Char ranges

You can use endless ranges of Chars, and they will still be finite, because there are only 1114111 valid Unicode characters:




Attempt This Online!

In many cases where you would otherwise use a range such as ['a'..'z'], you may be able to leave off the ending item, if you know the input is constrained to a certain set of characters (e.g. only alphanumeric ASCII)


Avoid Data.List

The function are nice, but imports are costly. If you only need a single function, there are shorter alternatives:

import Data.List;intercalate -- too long
c#s=tail$(c:)=<<s            -- nice, especially when inlined
(tail.).(=<<).(:)            -- if you like unreadable mess
(tail.).((c:)=<<)            -- applied to c
import Data.List;intersperse -- too long
c#s=tail$do d<-s;[c,d]       -- nice
(c:).pure=<<tail             -- a function on the list

Check if the challenge allows for leading and/or trailing newlines. You may be able to omit the tail.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.