ExamplesBy LevelBy TopicLearning Paths
074 Advanced

074 — Bifunctor

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "074 — Bifunctor" functional Rust example. Difficulty level: Advanced. Key concepts covered: Functional Programming. A bifunctor is a type with two type parameters where both can be independently mapped. Key difference from OCaml: 1. **No stdlib trait**: Neither Rust nor OCaml has a standard `Bifunctor` trait/typeclass. Implement the functions per type. Haskell has `Data.Bifunctor`.

Tutorial

The Problem

A bifunctor is a type with two type parameters where both can be independently mapped. Result<T, E> is the canonical bifunctor: map transforms the T (success side), map_err transforms the E (error side), and bimap applies both transformations simultaneously.

Other bifunctors: tuples (A, B) where both elements can be transformed independently, and Either<L, R> (common in Haskell and Scala). Bifunctors formalize the observation that both sides of a sum type or product type are mappable channels.

This generalizes Result::map + Result::map_err into a single unified concept. Used in category theory, functional programming libraries (Haskell's Data.Bifunctor, Scala's cats.Bifunctor), and error-type transformation pipelines where you need to convert both success and error types simultaneously.

🎯 Learning Outcomes

  • • Understand a bifunctor as a type with two mappable channels
  • • Implement bimap(f, g) that transforms both sides of a Result
  • • Implement first(f) and second(g) as single-side maps
  • • Recognize (A, B) as a bifunctor: bimap(f, g) on a pair
  • • See how bimap generalizes map and map_err into a unified interface
  • Code Example

    #![allow(clippy::all)]
    // Stub — awaiting conversion from OCaml source.

    Key Differences

  • No stdlib trait: Neither Rust nor OCaml has a standard Bifunctor trait/typeclass. Implement the functions per type. Haskell has Data.Bifunctor.
  • **map as first**: Rust's Result::map is exactly bifunctor::first — it maps the left (Ok) side. map_err is bifunctor::second. Recognizing this shows Result's functor structure.
  • Pair bifunctor: (A, B) maps to (f(A), g(B)). Rust: let (new_a, new_b) = (f(a), g(b)). This is the product type bifunctor.
  • Laws: Bifunctor laws: bimap id id = id, bimap (f . g) (h . k) = bimap f h . bimap g k. These ensure bimap preserves function composition.
  • OCaml Approach

    OCaml's Result is a bifunctor with bimap defined by pattern matching:

    let bimap f g = function
      | Ok x -> Ok (f x)
      | Error e -> Error (g e)
    
    (* first = map on Ok side *)
    let first f r = bimap f Fun.id r
    
    (* second = map on Error side *)
    let second g r = bimap Fun.id g r
    
    (* pair bifunctor *)
    let bimap_pair f g (a, b) = (f a, g b)
    

    Result.map f r is exactly first f r. Result.map_error g r is second g r. The Bifunctor typeclass from Haskell is not part of OCaml stdlib but is trivially implemented per-type.

    Full Source

    #![allow(clippy::all)]
    // Stub — awaiting conversion from OCaml source.

    Deep Comparison

    OCaml vs Rust: Bifunctor

    Overview

    See the example.rs and example.ml files for detailed implementations.

    Key Differences

    AspectOCamlRust
    Type systemHindley-MilnerOwnership + traits
    MemoryGCZero-cost abstractions
    MutabilityExplicit refmut keyword
    Error handlingOption/ResultResult<T, E>

    See README.md for detailed comparison.

    Exercises

  • Either type: Define enum Either<L, R> { Left(L), Right(R) }. Implement bimap, map_left, and map_right. Implement From<Result<R, L>> for Either<L, R>.
  • Profunctor: A profunctor is "contravariant in the first argument, covariant in the second". Implement dimap(f: B -> A, g: C -> D) -> impl Fn(A -> C) -> impl Fn(B -> D) for functions. This is the foundation of optics (lenses).
  • BiTraverse: Define bi_traverse(v: Result<T, E>, f: impl Fn(T) -> Option<T2>, g: impl Fn(E) -> Option<E2>) -> Option<Result<T2, E2>> that transforms both sides optionally.
  • Open Source Repos