ExamplesBy LevelBy TopicLearning Paths
240 Expert

Choice Profunctor

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "Choice Profunctor" functional Rust example. Difficulty level: Expert. Key concepts covered: Functional Programming. While `Strong` profunctors correspond to lenses (product types, always present), `Choice` profunctors correspond to prisms (sum types, possibly absent). Key difference from OCaml: 1. **Prism encoding**: `Choice` captures prisms just as `Strong` captures lenses; the duality (products vs. sums) maps onto profunctor classes.

Tutorial

The Problem

While Strong profunctors correspond to lenses (product types, always present), Choice profunctors correspond to prisms (sum types, possibly absent). Choice adds left: P<A, B> -> P<Either<A, C>, Either<B, C>> — the ability to "pass through" the Right(C) case while operating on Left(A) -> Left(B). Functions implement Choice naturally. A Van Laarhoven prism requires Choice: type Prism s a = ∀p. Choice p => p a b -> p s t.

🎯 Learning Outcomes

  • • Understand Choice as the profunctor class capturing prism-like behavior
  • • Learn left and right as the two Choice operations
  • • See how Choice enables "operate on one branch, pass through the other"
  • • Connect Choice to prisms: prisms are polymorphic over all Choice profunctors
  • Code Example

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

    Key Differences

  • Prism encoding: Choice captures prisms just as Strong captures lenses; the duality (products vs. sums) maps onto profunctor classes.
  • Function implementation: Functions implement both Strong and Choice — functions can thread both product and sum context; they are simultaneously lenses and prisms (isos) in the profunctor encoding.
  • Traversal: Traversal corresponds to profunctors that are both Strong and Choice with certain coherence conditions.
  • Wander: More general traversals require a Wander class beyond Choice; the full hierarchy is Profunctor ⊂ Strong ⊂ Wander.
  • OCaml Approach

    OCaml's Choice profunctor:

    module type CHOICE = sig
      include PROFUNCTOR
      val left : ('a, 'b) t -> (('a, 'c) Either.t, ('b, 'c) Either.t) t
      val right : ('a, 'b) t -> (('c, 'a) Either.t, ('c, 'b) Either.t) t
    end
    

    A prism type ('s, 't, 'a, 'b) prism = { run : 'p. (module CHOICE with type ('a,'b) t = 'p) -> 'p a b -> 'p s t } requires first-class module polymorphism — OCaml handles this better than Rust.

    Full Source

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

    Deep Comparison

    OCaml vs Rust: Choice Profunctor

    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

  • Implement left for a Kleisli<Option, A, B> profunctor — threading Option context through choice.
  • Write prism_via_choice(preview, review) creating a prism from the Choice profunctor encoding.
  • Verify that right(f) = dimap(Either::swap, Either::swap)(left(f))right is derivable from left.
  • Open Source Repos