コンテンツにスキップ

Identity Functor and Identity Monad

We stand at a significant juncture in our exploration of functional programming. Having journeyed through functions, types, algebraic structures like Monoids, and the general concepts of Functors and Monads, we are now poised to uncover a profound truth about a tool we’ve used from the very beginning:

  • pipeline operator |>

This chapter is a culmination, revealing that the intuitive act of pipelining functions is not just a convenience but is deeply intertwined with the very essence of Functors and Monads in their most fundamental forms.

We will now formally introduce and explore these:

  • Identity Functor
  • Identity Monad

Prepare for a moment of surprise and deep insight as we connect the dots!

In Unit 1, Section 4, we encountered the flip function, a higher-order function (HOF) that swaps the first two arguments of another function.

let flip = fun f x y -> f y x

Its type signature is generally expressed using F# generic type parameters as:

flip: ('a -> 'b -> 'c) -> 'b -> 'a -> 'c

image

We saw how flip could be used, for example, with an operator like (-) to create more pipeline-friendly versions. The expression value |> (flippedOperator arg1) became a way to structure operations. This hints at a deeper connection between pipelining, flip, and HOFs.

The pipeline operator x |> f is defined as simple function application: f x.

|> is a binary operator that takes two arguments:

  • x: 'a
  • f: 'a -> 'b

The HOF type signature is:

(|>) : 'a -> ('a -> 'b) -> 'b

image

Let’s examine what happens when we apply flip to the pipeline operator:

let flippedPipeOp = flip (|>)

Given:

  • flip: ('x -> 'y -> 'z) -> 'y -> 'x -> 'z
  • (|>): 'a -> ('a -> 'b) -> 'b

The resulting type of flippedPipeOp is:

('a -> 'b) -> 'a -> 'b

image

Now, let’s consider the behavior of this flippedPipeOp.

Key Insight:

This HOF takes a function f: 'a -> 'b and returns f itself.

In other words, flippedPipeOp is equivalent to fun f -> f.

It’s the identity HOF for functions of type 'a -> 'b.

image

image

This is a crucial observation that will connect directly to our first specific Functor example.

Introducing the Identity Functor and its map Operation

Section titled “Introducing the Identity Functor and its map Operation”

Let’s define the simplest possible Functor: the Identity Functor.

In this Functor, the “container” doesn’t actually change or add anything to the type it holds.

We can define it as: Id<'a> = 'a

Let’s examine its core aspects and operations, mirroring how we will look at the Identity Monad:

  1. The Identity Constructor (or ID for Functor context):

    • Purpose: To “place” a value of type 'a into the Identity Functor context. Given Id<'a> = 'a, this is an identity transformation.
    • Type: 'a -> Id<'a>
    • Behavior: Since Id<'a> is just 'a, this operation is simply the identity function: fun x -> x.
  2. The map operation (as HOF mapIdHOF):

    • Purpose: To take a function f: 'a -> 'b and, in the context of the Identity Functor, return a function that is effectively f itself, as no “unwrapping” or “rewrapping” is needed. For comparison with flip (|>), this means mapIdHOF returns its input function f directly.
    • General Type (for mapIdHOF view): ('a -> 'b) -> ('a -> 'b) (Derived from the standard Functor map type ('a -> 'b) -> F<'a> -> F<'b>, which for Id becomes ('a -> 'b) -> 'a -> 'b.
    • Behavior: mapIdHOF f = f

This map operation (specifically, mapIdHOF) precisely matches flip (|>) in both type and behavior.

This is a key realization: flip (|>) IS the map operation of the Identity Functor! The act of flipping the pipeline operator reveals the very essence of how map behaves in this fundamental context – it’s an identity transformation on the function itself.

Introducing the Identity Monad and its bind Operation

Section titled “Introducing the Identity Monad and its bind Operation”

Similar to the Identity Functor, we can define the Identity Monad. Again, the monadic type Id<'a> is simply an alias for 'a: Id<'a> = 'a

Let’s examine its core operations:

  1. The ID operation (often called return or unit):

    • Purpose: To take a normal value and put it into the Monad.
    • Type: 'a -> Id<'a>
    • Behavior: Since Id<'a> is just 'a, this operation is simply the identity function: 'a -> 'a.
  2. The bind operation:

    • Purpose: To take a monadic value and a function that returns a new monadic value, and compose them.
    • General Type: ('a -> M<'b>) -> M<'a> -> M<'b>
    • For Id: ('a -> Id<'b>) -> Id<'a> -> Id<'b>
    • Simplified for Id: Given Id<'x> = 'x, the type becomes ('a -> 'b) -> 'a -> 'b. (Here, the Kleisli arrow 'a -> Id<'b> is effectively 'a -> 'b in the Identity context).
    • Behavior: bindId k x = k x (where k is the function 'a -> Id<'b>, and x is the value of type Id<'a>).

The Grand Unification: Pipeline operator, Identity Functor’s map, and Identity Monad’s bind

Section titled “The Grand Unification: Pipeline operator, Identity Functor’s map, and Identity Monad’s bind”

Let’s explicitly bring together our key findings:

  1. Pipeline Operator |>:

    • Type: 'a -> ('a -> 'b) -> 'b
    • Behavior: x |> f = f x
  2. flip (|>):

    • Type: ('a -> 'b) -> 'a -> 'b
    • Behavior: This HOF is fun f -> f (the identity HOF for functions).
  3. Identity Functor’s map:

    • Type: ('a -> 'b) -> 'a -> 'b
    • Behavior: map f = f.
  4. Identity Monad’s bind:

    • Type: ('a -> 'b) -> 'a -> 'b
    • Behavior: bind f = f.

The Integration: Functor, Monad, and the Pipeline

Section titled “The Integration: Functor, Monad, and the Pipeline”

Therefore, these are fundamentally identical:

  • Pipeline Operation
  • Identity Functor
  • Identity Monad

The generic Fuctor/Monad on the generic Container:

Section titled “The generic Fuctor/Monad on the generic Container:”

image

The Identiy Functor/Monad on the Identity Container:

Section titled “The Identiy Functor/Monad on the Identity Container:”

image

The Pipeline as the Identity Function for Function:

Section titled “The Pipeline as the Identity Function for Function:”

image

image

All of the above are identical.

Functors and Monads are not distant, arcane concepts. Their most basic forms, the Identity Functor and Identity Monad, are already present in the very fabric of simple function application and the pipeline that we use constantly.

This provides a solid conceptual bridge: these advanced abstractions generalize core, familiar ideas to work across more complex scenarios. This is one of the great “Aha!” moments in understanding the mathematical underpinnings of functional programming.