What do people use the identity function for? [duplicate]
Asked Answered
D

8

18

For example, Haskell has the id function, Julia has the identity function, and many questions on SO deal with the identity function. (I suppose in Python you can do lambda x:x)

I've been busting my brain trying to think of a use case for this function; and I've failed.

What is the purpose of the identity function, and what are its common use-cases?

Depreciation answered 23/9, 2014 at 4:48 Comment(2)
Without id composition . is so lonely. Together they are a monoid: id . f == f == f . id, f . (g . h) == (f . g) . h. So you will see id used in cases where . is used.Follicle
I would point out that this question is a duplicate only if viewed as a Haskell question. Julia doesn't use the function at all the way Haskell does, so all the Haskell answers here are largely irrelevant. Only one answer here actually addresses the use of identity in Julia.Leaden
L
18

Remember that in Haskell functions are first class values, and can be used as data the same way as other values, and passed as arguments to other functions. Often you build the functions you really want to use by applying other functions to each other. Occasionally you will find that the function you want to use in a spot happens to be nothing more complicated than id.

For example, here is a function that negates every second element of a list:

negateEverySecond = zipWith id (cycle [id, negate])
Lizabethlizard answered 23/9, 2014 at 5:14 Comment(3)
The first use of id might be easier to understand if written ($).Frutescent
A simpler example: fromMaybe default = maybe default id.Greeneyed
I'm going to accept this, only because this was the answer that "clicked". The other one's are awesome too, and I'll +1 them.Depreciation
O
11

In Julia (in the standard library): A quick grep shows that currently the most prominent use of the identity function is as the default value of the by argument for various sorting related functions, such as sort!, sort, issorted, etc.

The by argument allows to specify a function to extract a sorting key from each object, so that two objects a and b are compared according to by(a) < by(b) rather than a < b. The identity function is natural as the default, since identity(a) < identity(b) is just the same as a < b.

There is also some internal sorting code that is special-cased for when by is identity, which should allow more efficient code for the common case.

Orvie answered 23/9, 2014 at 8:6 Comment(0)
F
8

id can be a good place to start when building up functions. For instance,

foldl f init xs = foldr (\x r -> \acc -> r (f acc x)) id xs $ init

chi mentions

type K a = (a -> Int) -> Int
-- factorial, CPS
factCPS :: Int -> K Int
factCPS 0 k = k 1
factCPS n k = factCPS (n-1) (k . (*n))

-- factorial, plain
fact :: Int -> Int
fact n = factCPS n id

This approach is actually closely related to the example I give above; the id in the definition of foldl is really just the continuation to use.

fact q
  = foldl (*) 1 [1..q]
  = foldr (\x r -> \acc -> r (acc * x)) id (build
      (\c n -> if q<1
               then n
               else let go k | k <= q = k `c` go (k+1)
                             | otherwise = n
                    in go 1)) 1
  -- foldr/build
  = (if q < 1
     then id
     else let go k | k <= q = \acc -> go (k+1) (acc * k)
                   | otherwise = id = \acc -> acc
          in go 1) 1
  = (if q < 1
     then id
     else let go k acc | k <= q = go (k+1) (acc * k)
                       | otherwise = acc
          in go 1) 1
  = if q < 1
    then 1
    else let go k acc | k <= q = go (k+1) (acc*k)
                      | otherwise = acc
         in go 1 1
Frutescent answered 23/9, 2014 at 5:24 Comment(2)
Or composition of a list of functions: foldr (.) id.Manikin
@augustss, that excellent point appears in chi's answer already.Frutescent
H
6

Mostly, you'd use it to return the exact value of the arguments unchanged in a function. For instance, in maybe.

Another example that I see is in (id &&& id) - ie. duplicating an element into a tuple.

λ> let l = [5,3,4,1]
λ> map (id &&& id) l
[(5,5),(3,3),(4,4),(1,1)]
Hustings answered 23/9, 2014 at 4:59 Comment(3)
But how is that better than map (\x->(x,x)) l? Is that just the canonical way of doing things in Haskell?Depreciation
@PythonNut, as I got more comfortable with higher order functions, I noticed that lambdas were just more cumbersome to work with. With this approach I can easily write composable variations such as (id &&&) -- a function of type (a -> b) -> (a -> (a,b)), modifying a function to include its argument in its result.Hema
@Depreciation You must avoid lambdas to write in pointfree style. Some people find this to be a more elegant or fun way to program, or just like to show off.Laskowski
B
4

Composing a list of functions:

compose :: [a -> a] -> a -> a
compose = foldr (.) id
-- to be compared with
sum :: Num a => [a] -> a
sum = foldr (+) 0

Appending messages, conditionally: (non the best solution, still not too bad either)

-- string=""
-- if foo: string += "foo"
-- if bar: string += "bar"
-- print string
putStrLn .
   (if bar then (++"bar") else id) .
   (if foo then (++"foo") else id) $
   ""

Continuation passing style, base case:

type K a = (a -> Int) -> Int

-- factorial, CPS
factCPS :: Int -> K Int
factCPS 0 k = k 1
factCPS n k = factCPS (n-1) (k . (*n))

-- factorial, plain
fact :: Int -> Int
fact n = factCPS n id
Bubonocele answered 23/9, 2014 at 7:57 Comment(0)
C
3

A reasonably common task is to get a value out of a Maybe. We'd like to write

fromMaybe :: a -> Maybe a -> a

although it is a standard, built-in function. Another standard built-in function is maybe

maybe :: b -> (a -> b) -> Maybe a -> b
maybe nothing just m = case m of
  Nothing -> nothing
  Just a  -> just a

which can be seen as the universal way of destructing a Maybe—it just passes one function for each branch of the pattern match.

Finally, it ought to be easy to see that fromMaybe and maybe are related:

fromMaybe default m = maybe default id m

or in more point-free style

fromMaybe = flip maybe id
Calvinna answered 23/9, 2014 at 6:33 Comment(0)
K
3

I often try to use identity-like functions in my design as a default argument for transformation functions in order to keep generality and modularity as high as possible.

It can happen that today you want a processing pipeline that operates with your original data, so you don't need any preliminary transformation on your input (pre_mapping = lambda x : x). In pseudo-python-code

 def do_something(data,pre_mapping = lambda x : x)
       data = pre_mapping(data)
       real_foo(data)

maybe tomorrow you realize that it is better to operate with e.g. the squares of the data and so, in a line, you call

 do_something(data
                 , pre_mapping = lambda x : x**2)

This becomes very useful e.g. if you need to try many of these pre_mappings.

Kelly answered 23/9, 2014 at 9:4 Comment(0)
B
3

Basically, any time a high-order function takes one or more functions as arguments, but you don't actually need to do any work, you can pass in id instead.

I think somebody already mentioned the maybe function: maybe 0 id will replace Nothing with 0, but if it's Just then we don't actually want to change the data, just return it. So we pass in id.

A similar function is const: for example, const 7 is a function that takes one argument, utterly ignores it, and returns 7. Why the hell would you ever want that? Well, consider this implementation of length:

length = sum . map (const 1)

That is, replace every element with 1, and then sum them all.

Bromo answered 23/9, 2014 at 9:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.