63

Which are the uses for id function in Haskell?

fjsj
  • 10,995
  • 11
  • 41
  • 57

9 Answers9

83

It's useful as an argument to higher order functions (functions which take functions as arguments), where you want some particular value left unchanged.

Example 1: Leave a value alone if it is in a Just, otherwise, return a default of 7.

Prelude Data.Maybe> :t maybe
maybe :: b -> (a -> b) -> Maybe a -> b

Prelude Data.Maybe> maybe 7 id (Just 2)
2

Example 2: building up a function via a fold:

Prelude Data.Maybe> :t foldr (.) id [(+2), (*7)]
:: (Num a) => a -> a

Prelude Data.Maybe> let f = foldr (.) id [(+2), (*7)]

Prelude Data.Maybe> f 7
51

We built a new function f by folding a list of functions together with (.), using id as the base case.

Example 3: the base case for functions as monoids (simplified).

instance Monoid (a -> a) where
        mempty        = id
        f `mappend` g = (f . g)

Similar to our example with fold, functions can be treated as concatenable values, with id serving for the empty case, and (.) as append.

Example 4: a trivial hash function.

Data.HashTable> h <- new (==) id :: IO (HashTable Data.Int.Int32 Int)

Data.HashTable> insert h 7 2

Data.HashTable> Data.HashTable.lookup h 7
Just 2

Hashtables require a hashing function. But what if your key is already hashed? Then pass the id function, to fill in as your hashing method, with zero performance overhead.

Don Stewart
  • 137,316
  • 36
  • 365
  • 468
  • Would it be better to use `foldl' (.) id` over `foldr (.) id`? – Thomas Eding Jun 28 '10 at 22:52
  • 2
    Not in this case. Strictness won't have any effect on function composition. – Don Stewart Jun 28 '10 at 23:10
  • @Don Stewart, I hope compiler doing something terrible wrong with some functions (like `(+)` and other foreign-functions which requires both arguments to be bound) like changing tree of application to fold some nodes and free memory used by them. – ony Jun 29 '10 at 06:46
  • There is a difference: `foldl' (.) id (repeat (const 0)) 0` vs. `foldr (.) id (repeat (const 0)) 0`. – sdcvvc Jun 29 '10 at 07:35
  • sdcvvc, well spotted, but foldl has the same property. (the ' isn't the issue). – Don Stewart Jun 29 '10 at 08:15
  • 2
    Another example related to this is using `id` as the base function in Continuation Passing Style (CPS). – John L Jun 29 '10 at 22:12
  • 11
    One of my favorites: `numberOfTrues :: [Bool] -> Int; numberOfTrues = length . filter id` – luqui Jun 30 '10 at 11:36
  • @luqui, another fun way to write that one is `numberOfTrues = sum . map fromEnum`. – dfeuer Sep 23 '14 at 17:02
73

If you manipulate numbers, particularly with addition and multiplication, you'll have noticed the usefulness of 0 and 1. Likewise, if you manipulate lists, the empty list turns out to be quite handy. Similarly, if you manipulate functions (very common in functional programming), you'll come to notice the same sort of usefulness of id.

Conal
  • 18,517
  • 2
  • 37
  • 40
25

In functional languages, functions are first class values that you can pass as a parameter. So one of the most common uses of id comes up when you pass a function as a parameter to another function to tell it what to do. One of the choices of what to do is likely to be "just leave it alone" - in that case, you pass id as the parameter.

Yitz
  • 5,057
  • 24
  • 19
  • 6
    `just leave it alone` is the part that made me click, thanks! – Whitebird Aug 10 '15 at 18:46
  • _One of the choices of what to do is likely to be "just leave it alone" - in that case, you pass id as the parameter_ Do you mean polymorphic behavior of function - Meaning HOF was already accepting some function as argument with signature T->T i.e input and o/p type are same and under certain condition you can pass Id function which also have i/p and o/p of same type plus same value even ? – rahulaga-msft Mar 23 '19 at 05:03
8

Suppose you're searching for some kind of solution to a puzzle where you make a move at each turn. You start with a candidate position pos. At each stage there is a list of possible transformations you could make to pos (eg. sliding a piece in the puzzle). In a functional language it's natural to represent transformations as functions so now you can make a list of moves using a list of functions. If "doing nothing" is a legal move in this puzzle, then you would represent that with id. If you didn't do that then you'd need to handle "doing nothing" as a special case that works differently from "doing something". By using id you can handle all cases uniformly in a single list.

This is probably the reason why almost all uses of id exist. To handle "doing nothing" uniformly with "doing something".

sigfpe
  • 7,996
  • 2
  • 27
  • 48
  • Do you mean polymorphic behavior of function - Meaning HOF was already accepting some function as argument with signature T->T i.e input and o/p type are same and under certain condition you can pass Id function which also have i/p and o/p of same type plus same value even ? – rahulaga-msft Mar 23 '19 at 05:04
3

Since we are finding nice applications of id. Here, have a palindrome :)

import Control.Applicative

pal :: [a] -> [a]
pal = (++) <$> id <*> reverse
Shyam Bhimani
  • 1,310
  • 1
  • 22
  • 37
raichoo
  • 2,557
  • 21
  • 28
  • 1
    @MiguelRodrigues here:) `<*> reverse` suggests `instance Applicative ((->) r)` where `(f <*> g) x = f x (g x)` and `($) = (.)` so `pal x = ((++).id) x (reverse x) = x ++ reverse x`. (this shows `pal = (++) <*> reverse` also works). – Will Ness Oct 05 '14 at 14:58
3

For a different sort of answer:

I'll often do this when chaining multiple functions via composition:

foo = id
  . bar
  . baz
  . etc

over

foo = bar
  . baz
  . etc

It keeps things easier to edit. One can do similar things with other 'zero' elements, such as

foo = return
  >>= bar
  >>= baz

foos = []
  ++ bars
  ++ bazs
Thomas Eding
  • 35,312
  • 13
  • 75
  • 106
  • 4
    Clever, but do you *really* do this in real code? This almost makes me long for Java. – jrockway Jun 28 '10 at 23:54
  • I do it in real code if the compositions don't fit neatly onto one line. This typically happens when the function name is long and/or there are many pointful parameters. – Thomas Eding Jun 29 '10 at 00:52
3

Imagine you are a computer, i.e. you can execute a sequence of steps. Then if I want you to stay in your current state, but I always have to give you an instruction (I cannot just mute and let the time pass), what instruction do I give you? Id is the function created for that, for returning the argument unchanged (in the case of the previous computer the argument would be its state) and for having a name for it. That necessity appears only when you have high order functions, when you operate with functions without considering what's inside them, that forces you to represent symbolically even the "do nothing" implementation. Analogously 0 seen as a quantity of something, is a symbol for the absence of quantity. Actually in Algebra both 0 and id are considered the neutral elements of the operations + and ∘ (function composition) respectively, or more formally:

for all x of type number:

  • 0 + x = x
  • x + 0 = x

for all f of type function:

  • id ∘ f = f
  • f ∘ id = f
lamg
  • 364
  • 2
  • 6
2

I can also help improve your golf score. Instead of using

($)

you can save a single character by using id.

e.g.

zipWith id [(+1), succ] [2,3,4]

An interesting, more than useful result.

mrehayden
  • 181
  • 1
  • 5
0

Whenever you need to have a function somewhere, but want to do more than just hold its place (with 'undefined' as an example).

It's also useful, as (soon-to-be) Dr. Stewart mentioned above, for when you need to pass a function as an argument to another function:

join = (>>= id)

or as the result of a function:

let f = id in f 10

(presumably, you will edit the above function later to do something more "interesting"... ;)

As others have mentioned, id is a wonderful place-holder for when you need a function somewhere.

BMeph
  • 1,472
  • 11
  • 16