2

I can't seem to understand how id is used as a parameter for some higher order functions. I've read somewhere that it is used to "leave something alone" but can't seem to get my head around it.

For example, why is the type of liftA2 id f (b -> c) -> f b -> f c?

Also, why can't I pass id to g for

g :: (Int -> Int -> Int) -> Int -> Int -> Int

?

Shaurya Gupta
  • 327
  • 3
  • 14
  • `liftA2 id` is an obfuscated version of `liftA2 ($)`. Indeed, when `id` is considered as a *binary* function, it is better written as `($)`. – chi Mar 14 '18 at 15:01

1 Answers1

5

I've read somewhere that [id] is used to "leave something alone" but can't seem to get my head around it.

id is used to "leave something alone" because that is all it does -- that is, nothing:

id :: x -> x
id x = x

That being so, id often shows up as a do-nothing placeholder passed to higher-order functions. fmap id foo doesn't actually change the values found within foo (in fact, fmap id = id, which is the first functor law), id . f and f . id are both equivalent to f, and so forth. A more interesting example is the classic trick of defining foldl using foldr, in which id is used as a base case for building a function with foldr.

For example, why is the type of liftA2 id f (b -> c) -> f b -> f c?

The type of liftA2 is:

liftA2 :: (a -> b -> c) -> f a -> f b -> f c

In the case of liftA2 id, we have...

(a -> (b -> c)) ~ (x -> x)

... and so...

a ~ x
(b -> c) ~ x

... therefore...

liftA2 id :: f (b -> c) -> f b -> f c

(As you probably know, liftA2 id = (<*>). What is going on is perhaps more obvious if you write it as liftA2 ($), ($) :: (a -> b) -> (a -> b) being merely a specialised id.)

Also, why can't I pass id to g for

g :: (Int -> Int -> Int) -> Int -> Int -> Int

Because then you'd have to unify...

(Int -> (Int -> Int)) ~ (x -> x)

... which is impossible, as x cannot be both Int and Int -> Int.

Community
  • 1
  • 1
duplode
  • 33,731
  • 7
  • 79
  • 150
  • 2
    For an intuitive reading of "you'd have to unify...", try this one. `id` can leave an `Int` alone and have type `Int -> Int` (this has the same argument type as `g`'s argument); or `id` can leave an `Int -> Int` function alone and have type `(Int -> Int) -> (Int -> Int)` aka `(Int -> Int) -> Int -> Int` (this has the same result type as `g`'s argument). But, unlike with humans, if you try to leave two `Int`s alone, you don't get just one `Int` out the other end, so `id` can't have type `Int -> Int -> Int`. – Daniel Wagner Mar 14 '18 at 14:13