On this page:
2.1 Functor
2.1.1 Category of Categories
2.1.1.1 Isomorphism of Categories
2.1.2 Constant Functor
2.1.3 Opposite Functor
2.1.4 Essentially Injective Functor
2.1.5 Essentially Surjective Functor
2.1.6 Inclusion Functor
2.1.7 Projection Functor
2.1.8 Forgetful Functor
2.1.9 Free Monoid Functor
2.1.10 Free Category Functor
2.1.10.1 Category of Trees
2.1.10.2 Category of Forests
2.1.11 Composition Functor
2.1.12 (Co)Slice Functor
2.2 Diagram
2.3 𝐒𝐞𝐭-Valued Functor
2.3.1 Category of Elements of a Functor
2.3.2 Powerset Functor
2.3.2.1 Direct Image Functor
2.3.2.2 Preimage Functor
2.3.2.3 Universal Image Functor
2.3.3 Hom Functor
2.3.3.1 Covariant Hom Functor
2.3.3.2 Contravariant Hom Functor
2.3.3.3 Two-Variable Hom Functor
2.3.4 Cayley’s Theorem
2.3.5 Action
2.3.5.1 Monoid Actions as Functors
2.3.5.2 𝐒𝐞𝐭-Valued Functors as Typed Actions
2.4 Finite Automaton
2.4.1 Deterministic Finite Automaton
2.4.2 Typed Deterministic Finite Automaton
2.4.3 Nondeterministic Finite Automaton
8.16.0.1

2 Functor🔗

In this chapter, we introduce the concept of functors. Functors are fundamental in category theory, as they map both objects and morphisms between categories while preserving the structural relationships between them.

To clarify the explanation and show how functors apply to programming, we will use Typed Racket syntax. Specifically, we will opt for #lang typed/racket/no-check instead of #lang racket. This choice allows us to incorporate type annotations for improved code clarity and better illustration of functorial properties.

    2.1 Functor

      2.1.1 Category of Categories

        2.1.1.1 Isomorphism of Categories

      2.1.2 Constant Functor

      2.1.3 Opposite Functor

      2.1.4 Essentially Injective Functor

      2.1.5 Essentially Surjective Functor

      2.1.6 Inclusion Functor

      2.1.7 Projection Functor

      2.1.8 Forgetful Functor

      2.1.9 Free Monoid Functor

      2.1.10 Free Category Functor

        2.1.10.1 Category of Trees

        2.1.10.2 Category of Forests

      2.1.11 Composition Functor

      2.1.12 (Co)Slice Functor

    2.2 Diagram

    2.3 𝐒𝐞𝐭-Valued Functor

      2.3.1 Category of Elements of a Functor

      2.3.2 Powerset Functor

        2.3.2.1 Direct Image Functor

        2.3.2.2 Preimage Functor

        2.3.2.3 Universal Image Functor

      2.3.3 Hom Functor

        2.3.3.1 Covariant Hom Functor

        2.3.3.2 Contravariant Hom Functor

        2.3.3.3 Two-Variable Hom Functor

      2.3.4 Cayley’s Theorem

      2.3.5 Action

        2.3.5.1 Monoid Actions as Functors

        2.3.5.2 𝐒𝐞𝐭-Valued Functors as Typed Actions

    2.4 Finite Automaton

      2.4.1 Deterministic Finite Automaton

      2.4.2 Typed Deterministic Finite Automaton

      2.4.3 Nondeterministic Finite Automaton

2.1 Functor🔗

Functors are sometimes called category homomorphisms.

Just as functions map elements between sets, functors provide a way to map objects and morphisms between categories. This mapping preserves the structural aspects of categories.

For convenience, if a is an object in 𝒞, then F(a) = F0(a); if f is a morphism in 𝒞, then F(f) = F1(f).

Similar to how a category 𝒞 is defined by two collections 𝒞0 and 𝒞1, a functor F from 𝒞 to 𝒟, denoted by F : 𝒞 → 𝒟, is also defined by two functions F0 : 𝒞0 → 𝒟0 and F1 : 𝒞1 → 𝒟1. These functions must satisfy the following properties:

  1. Preservation of domains and codomains

    For any morphism f : a → b : 𝒞, there must exist a corresponding morphism F(f) : F(a) → F(b) : 𝒟.

    [picture] F-1.svg

  2. Preservation of identity morphisms

    For any object a : 𝒞, F(ida) = idF(a).

    [picture] F-2.svg

  3. Preservation of composable pairs

    If (f, g) is a composable pair in 𝒞, then (F(f), F(g)) is a composable pair in 𝒟, and F(g∘f) = F(g)∘F(f).

    [picture] F-3.svg

Exercise: Show that a function between two sets can be viewed as a functor between two discrete categories.

A functor F : 𝒞 → 𝒟 is like a digraph homomorphism that also preserves composable pairs and identity morphisms. To illustrate F, let’s consider a function F2 : 𝒞2 → 𝒟2. Using F2, we can describe F with the following diagram:

[picture] functor.svg

The proof is left as an exercise.

We can see that each similarly labeled square in the diagram is a commutative square. Additionally, the commutative squares show the properties of functors.

To verify the properties of functors, we define some check procedures to automate the testing of essential properties a functor preserves:

"code/functor/check.rkt"

#lang racket/base
 
(require rackunit)
(provide (all-defined-out))
 
(define (check-ftr 𝒞 𝒟)
  (define-values (dom𝒞 cod𝒞 ∘𝒞 ?𝒞 =𝒞) (𝒞))
  (define-values (dom𝒟 cod𝒟 ∘𝒟 ?𝒟 =𝒟) (𝒟))
  (λ (F)
    (λ (a b c f g)
      (check-pred ?𝒞 a) (check-pred ?𝒟 (F a))
      (check-pred ?𝒞 b) (check-pred ?𝒟 (F b))
      (check-pred ?𝒞 c) (check-pred ?𝒟 (F c))
      (check-pred ?𝒞 f) (check-pred ?𝒟 (F f))
      (check-pred ?𝒞 g) (check-pred ?𝒟 (F g))
 
      ;; Preservation of domain and codomain
      (check-true (=𝒟 (F a) (dom𝒟 (F f)) (F (dom𝒞 f))))
      (check-true (=𝒟 (F b) (cod𝒟 (F f)) (F (cod𝒞 f))))
 
      ;; Preservation of identity morphisms
      (check-true (=𝒞    c  (dom𝒞    c)  (cod𝒞    c)))
      (check-true (=𝒟 (F c) (dom𝒟 (F c)) (cod𝒟 (F c))))
 
      ;; Preservation of composable pairs
      (check-true (=𝒟 (∘𝒟 (F g) (F f)) (F (∘𝒞 g f)))))))
 

The following example illustrates how to implement functors in Racket:

"code/functor/Set->Rel.rkt"

#lang typed/racket/base/no-check
 
(require "../category/Set.rkt"
         "../category/Rel.rkt"
         racket/promise)
 
(: 𝒮 𝐂𝐚𝐭)
(: dom𝒮 ( ([a : 𝒮] [b : 𝒮]) ( (→𝒮 a b) a)))
(: cod𝒮 ( ([a : 𝒮] [b : 𝒮]) ( (→𝒮 a b) b)))
(: ∘𝒮 ( ([a : 𝒮] [b : 𝒮] [c : 𝒮] ... [z : 𝒮]) ( (× (→𝒮 a b) (→𝒮 b c) ...) (→𝒮 a z))))
(: ?𝒮 (pred ( ([a : 𝒮] [b : 𝒮]) (→𝒮 a b))))
(: =𝒮 ( ([a : 𝒮] [b : 𝒮] [c : 𝒮] [d : 𝒮] ...) ( (× (→𝒮 a b) (→𝒮 c d) ...) Boolean)))
(define (𝒮 m) m)
(define-values (dom𝒮 cod𝒮 ∘𝒮 ?𝒮 =𝒮) (𝐒𝐞𝐭))
 
(:  𝐂𝐚𝐭)
(: domℛ ( ([a : ] [b : ]) ( (→ℛ a b) a)))
(: codℛ ( ([a : ] [b : ]) ( (→ℛ a b) b)))
(: ∘ℛ ( ([a : ] [b : ] [c : ] ... [z : ]) ( (× (→ℛ a b) (→ℛ b c) ...) (→ℛ a z))))
(: ?ℛ (pred ( ([a : ] [b : ]) (→ℛ a b))))
(: =ℛ ( ([a : ] [b : ] [c : ] [d : ] ...) ( (× (→ℛ a b) (→ℛ c d) ...) Boolean)))
(define ( m) m)
(define-values (domℛ codℛ ∘ℛ ?ℛ =ℛ) (𝐑𝐞𝐥))
 
;; Functors from 𝒮 to 
(: F ( ([a : 𝒮] [b : 𝒮]) ( (→𝒮 a b) (→ℛ (F a) (F b)))))
(define F
  (let ()
    (define (F.map m) (for/set ([(x y) (in-hash m)]) (cons x y)))
    (λ (f)
      (define a (dom𝒮 f))
      (define b (cod𝒮 f))
      (define a.map (function-map a))
      (define b.map (function-map b))
      (define f.map (function-map f))
      (define Fa (relation (lazy Fa) (lazy Fa) (F.map a.map)))
      (define Fb (relation (lazy Fb) (lazy Fb) (F.map b.map)))
      (define Ff (relation (lazy Fa) (lazy Fb) (F.map f.map)))
      Ff)))
 
(module+ test
  (require "check.rkt")
 
  ;; Objects in 𝒮
  (: a 𝒮) (define a (function (lazy a) (lazy a) #hash([x0 . x0] [x1 . x1])))
  (: b 𝒮) (define b (function (lazy b) (lazy b) #hash([y0 . y0] [y1 . y1])))
  (: c 𝒮) (define c (function (lazy c) (lazy c) #hash([z0 . z0] [z1 . z1])))
 
  ;; Morphisms in 𝒮
  (: f (→𝒮 a b)) (define f (function (lazy a) (lazy b) #hash([x0 . y0] [x1 . y0])))
  (: g (→𝒮 b c)) (define g (function (lazy b) (lazy c) #hash([y0 . z0] [y1 . z0])))
 
  (define check-𝐒𝐞𝐭→𝐑𝐞𝐥 (check-ftr 𝐒𝐞𝐭 𝐑𝐞𝐥))
  (define check-F (check-𝐒𝐞𝐭→𝐑𝐞𝐥 F))
  (check-F a b c f g))
 

See more in nLab.

The functors discussed in this tutorial default to the covariant functors. A contravariant functor from 𝒞 to 𝒟 can be considered as a covariant functor from 𝒞op to 𝒟. For example, matrix-transpose is a classical example of a contravariant functor:

"code/functor/transpose.rkt"

#lang typed/racket/base/no-check
 
(require "../category/Matr.rkt"
         "../category/dual.rkt"
         math/matrix)
 
(: T ( ([b : ] [a : ]) ( (→ℳ a b) (→ℳ (T b) (T a)))))
(define T matrix-transpose)
 
(module+ test
  (require "check.rkt")
  (define (rand m n) (random 1 9))
  (define  compose)
 
  ;; Objects
  (: a ) (define a (identity-matrix 1))
  (: b ) (define b (identity-matrix 2))
  (: c ) (define c (identity-matrix 3))
  (: d ) (define d (identity-matrix 4))
 
  ;; Morphisms
  (: f (→ℳ a b)) (define f (build-matrix 2 1 rand))
  (: g (→ℳ b c)) (define g (build-matrix 3 2 rand))
  (: h (→ℳ c d)) (define h (build-matrix 4 3 rand))
 
  (define check-†𝐌𝐚𝐭𝐫→𝐌𝐚𝐭𝐫 (check-ftr (  𝐌𝐚𝐭𝐫) 𝐌𝐚𝐭𝐫))
  (define check-T (check-†𝐌𝐚𝐭𝐫→𝐌𝐚𝐭𝐫 T))
  (check-T c b a g f))
 

A functor F : 𝒞 → 𝒟 preserves a property P of morphisms in 𝒞 if whenever f has P, so does F(f). Conversely, a functor reflects a property P of morphisms in 𝒟 if whenever F(f) has P, so does f.

F may have additional properties:

If a functor is fully faithful, and injective on objects, it is called an embedding.

[picture] embedding.svg

Exercise: Prove that every faithful functor reflects monomorphisms.

Exercise: Prove that every faithful functor reflects epimorphisms.

Exercise: Let F : 𝒞 → 𝒟 be fully faithful. Prove that if F(a) = F(b), then a ≅ b and Hom𝒞(a, a) ≅ Hom𝒞(a, b) ≅ Hom𝒞(b, a) ≅ Hom𝒞(b, b).

2.1.1 Category of Categories🔗

Since functors are defined as a kind of function, it is natural to define the composition of functors in the same way as the composition of functions: G∘F(f) = G(F(f)). This allows us to treat functors as morphisms, leading to the construction of the category of categories, denoted as 𝐂𝐚𝐭, which forms a higher-level structure.

Exercise: Prove that the composition of functors is associative.

In practical implementations using Racket, we’ll employ 𝐏𝐫𝐨𝐜 to symbolize 𝐂𝐚𝐭. This is because, in Racket, we implement functors as procedures. Note that since the task of comparing procedure functionality can only be done by the programmer, we’ll avoid using = or just use it as pseudocode.

Although we have given specific Category Examples, these examples are just one way to implement the corresponding concepts. We can define these concepts in other ways as well. These different implementations of the same concept can be seen as equivalent objects in 𝐂𝐚𝐭.

Therefore, in the following sections, when we discuss specific categories, their definitions might differ from the Racket code in the previous sections. For instance, in the Category of Sets, morphisms of 𝐒𝐞𝐭 are defined as hash tables, but essentially they are mappings and might be defined as procedures in later sections.

2.1.1.1 Isomorphism of Categories🔗

In 𝐒𝐞𝐭, an isomorphism is a function that establishes a one-to-one correspondence between elements of two sets. Similarly, in 𝐂𝐚𝐭, an isomorphism is a functor that establishes a one-to-one correspondence between objects and morphisms in two categories, i.e., an embedding that is surjective on objects.

[picture] iso.svg

An endofunctor is an endomorphism in 𝐂𝐚𝐭, while an autofunctor is an automorphism in 𝐂𝐚𝐭.

Exercise: Prove that every invertible functor is an embedding.

In 𝐂𝐚𝐭, a terminal object 1 is a discrete category with exactly one object, and the initial object 0 is the category with no objects and no morphisms.

Exercise: Prove 𝒞 ≅ 1×𝒞 ≅ 𝒞×10+𝒞 ≅ 𝒞+0.

Exercise: Prove 00×𝒞 ≅ 𝒞×0.

Exercise: Prove that if there is a functor from 𝒞 to 0, then 𝒞 = 0.

Exercise: For objects 𝒞 and 𝒟 in 𝐂𝐚𝐭. Prove 𝒞×𝒟 ≅ 𝒟×𝒞 and 𝒞+𝒟 ≅ 𝒟+𝒞.

Exercise: For objects 𝒜, , and 𝒞 in 𝐂𝐚𝐭. Prove the distributive laws: 𝒜×(ℬ+𝒞) ≅ 𝒜×ℬ+𝒜×𝒞 and (𝒜+ℬ)×𝒞 ≅ 𝒜×𝒞+ℬ×𝒞.

Exercise: Show that both 𝒞 and 𝒟 are isomorphic to some quotient categories of 𝒞×𝒟 and some subcategories of 𝒞+𝒟.

2.1.2 Constant Functor🔗

A constant functor Δd : 𝒞 → 𝒟 is a functor that sends each object in 𝒞 to a fixed object d in 𝒟 and each morphism in 𝒞 to idd in 𝒟.

2.1.3 Opposite Functor🔗

For a functor F : 𝒞 → 𝒟, there is an opposite functor Fop : 𝒞op → 𝒟op such that Fop0 = F0 and Fop1 = F1.

2.1.4 Essentially Injective Functor🔗

A functor F : 𝒞 → 𝒟 is essentially injective if it is injective on objects up to isomorphism: ∀a, b ∈ 𝒞0, F(a) ≅ F(b) ⇒ a ≅ b.

Exercise: Prove that a functor is essentially injective iff it is injective on isomorphism classes.

2.1.5 Essentially Surjective Functor🔗

A functor F : 𝒞 → 𝒟 is essentially surjective if it is surjective on objects up to isomorphism: ∀d ∈ 𝒟0, ∃c ∈ 𝒞0, F(c) ≅ d.

2.1.6 Inclusion Functor🔗

The inclusion functor I : 𝒟 → 𝒞 is a functor that sends each object and each morphism in 𝒟 to 𝒞.

[picture] inc.svg

Exercise: Prove 𝒟 ⊆ 𝒞.

Exercise: Prove that if I is essentially surjective, then 𝒟 is a representative subcategory of 𝒞.

Exercise: Prove that every inclusion functor is a faithful functor.

Exercise: Prove that every inclusion functor from a full subcategory is fully faithful.

2.1.7 Projection Functor🔗

The projection functor (quotient functor) P : 𝒞 → 𝒞/∼ is a functor that ∀a ∈ 𝒞0, P(a) = [a] and ∀f ∈ 𝒞1, P(f) = [f].

Exercise: For a functor F : 𝒞 → 𝒟 and a congruence relation that ∀f, g ∈ 𝒞1, f ∼ g ⇒ F(f) = F(g). Prove that there is a unique functor F′ : 𝒞/∼ → 𝒟 for which F′∘P = F.

[picture] F~.svg

2.1.8 Forgetful Functor🔗

A forgetful functor (underlying functor or stripping functor) is a type of functor that forgets some or all of the structure of the objects and the structure-preserving functions in its domain category.

For example, if we forget morphisms in a category, then we get a set. Extending this idea, we get a forgetful functor U : 𝐂𝐚𝐭 → 𝐒𝐞𝐭, which forgets 𝒞1 and F1, but preserves 𝒞0 and F0: U0(𝒞) = 𝒞0 and U1(F) = F0.

[picture] U.svg

Exercise: Try to define a forgetful functor from 𝐌𝐨𝐧 to 𝐒𝐞𝐭.

Exercise: Try to define a forgetful functor from 𝐒𝐞𝐭 to 𝐒𝐞𝐭.

2.1.9 Free Monoid Functor🔗

The Kleene star (Kleene closure) A* of a set A is the set of all possible finite-length sequences of elements of A. The free monoid on A is defined as the tuple (A*, ∘, ()), where () is the empty sequence, and is the operation that concatenates two sequences.

The free monoid functor F : 𝐒𝐞𝐭 → 𝐌𝐨𝐧 maps any set to its corresponding free monoid. All free monoids obtained through F share the same and (), so we commonly use Kleene stars to represent them.

In Racket, we can represent the Kleene star as a list:

(define-type A* (Listof A))

Here, and () can be represented by append and null, respectively. Below is an example of defining the free monoid functor F in Racket:

(: F ( (A B) ( ( A B) ( A* B*))))
(define F (curry map))
2.1.10 Free Category Functor🔗

For each node a, there is a unique path of length 0, called the empty path at a.

The free category (path category) of a digraph 𝒢 is a category in which objects are the nodes of 𝒢, and morphisms are paths — that is, sequences of arrows in 𝒢, each connecting one node to the next in sequence.

In any category, we can derive an underlying digraph by abstracting away the composition and identity morphisms, focusing only on the nodes and the direct arrows that connect them. This transformation can be formally captured by a forgetful functor from 𝐂𝐚𝐭 to 𝐃𝐠𝐫, which forgets the category structure and retains only the basic digraph structure.

Exercise: Try to define the procedure make-path, which takes a digraph, a source node, and a Kleene star of arrow names, and returns the corresponding path.

The free category functor F : 𝐃𝐠𝐫 → 𝐂𝐚𝐭 maps each digraph to its corresponding free category.

Exercise: Try to implement F0 that constructs the free category for a given digraph 𝒢.

Since 𝐌𝐨𝐧 can be viewed as 𝐎𝐨𝐜, any set can also be viewed as a digraph with a single node. In this view, each element of the set corresponds to an arrow within this single node digraph. This analogy follows from the fact that we can derive the underlying set of a monoid through a forgetful functor, just as we can derive an underlying digraph from an OOC.

2.1.10.1 Category of Trees🔗

A rooted tree is a digraph in which the corresponding free category contains an initial object or terminal object, referred to as the root.

[picture] tree.svg

Exercise: Prove that a tree is has a unique root.

Trees are often used to represent hierarchical structures, where each node has exactly one incoming or outgoing path (except for the root, which only has an empty path).

The category of trees, denoted by 𝐓𝐫𝐞𝐞, has trees as objects and tree homomorphisms as morphisms

2.1.10.2 Category of Forests🔗

A rooted forest is a disjoint union of rooted trees.

The category of forests, denoted by 𝐅𝐬𝐭, has forests as objects and forest homomorphisms as morphisms.

Exercise: Prove that is a terminal object in 𝐅𝐬𝐭.

2.1.11 Composition Functor🔗

For morphism f : a → b : 𝒞 and g : b → c : 𝒞, there are two kinds of composition functors: f/𝒞 : b/𝒞 → a/𝒞 and 𝒞/g : 𝒞/b → 𝒞/c. f/𝒞 maps an object g in b/𝒞 to the object g∘f in a/𝒞, and 𝒞/g maps an object f in 𝒞/b to the object g∘f in 𝒞/c.

Exercise: Implement a composition functor f/ℳ : b/ℳ → a/ℳ, where is 𝐌𝐚𝐭𝐫 and f : a → b is a morphism in .

Exercise: Implement a composition functor ℳ/g : ℳ/b → ℳ/c, where is 𝐌𝐚𝐭𝐫 and g : b → c is a morphism in .

2.1.12 (Co)Slice Functor🔗

For a category 𝒞, the slice functor 𝒞/- maps each morphism g : b → c : 𝒞 to the composition functor 𝒞/g : 𝒞/b → 𝒞/c.

[picture] C%-.svg

(: 𝒞 𝐂𝐚𝐭)
(: 𝒞/- ( ([b : 𝒞] [c : 𝒞]) ( (→𝒞 b c) (→𝐂𝐚𝐭 𝒞/b 𝒞/c))))
(define (𝒞/- g)
  (: 𝒞/g ( ([x : 𝒞/b] [y : 𝒞/b]) ( (→𝒞/b x y) (→𝒞/c (∘𝒞 g x) (∘𝒞 g y)))))
  (define 𝒞/g
    (match-λ
      [`((,x) (,y ,z))
       `((,(∘𝒞 g x)) (,(∘𝒞 g y) ,z))]))
  𝒞/g)

Exercise: Prove 𝒞/g∘f = 𝒞/g∘𝒞/f.

For a category 𝒞, the coslice functor -/𝒞 maps each morphism f : a → b : 𝒞 to the composition functor f/𝒞 : b/𝒞 → a/𝒞.

[picture] -%C.svg

(: 𝒞 𝐂𝐚𝐭)
(: -/𝒞 ( ([b : 𝒞] [a : 𝒞]) ( (→𝒞 a b) (→𝐂𝐚𝐭 b/𝒞 a/𝒞))))
(define (-/𝒞 f)
  (: f/𝒞 ( ([x : b/𝒞] [y : b/𝒞]) ( (→b/𝒞 x y) (→a/𝒞 (∘𝒞 x f) (∘𝒞 y f)))))
  (define f/𝒞
    (match-λ
      [`((,z ,x) (,y))
       `((,z ,(∘𝒞 x f)) (,(∘𝒞 y f)))]))
  f/𝒞)

Exercise: Prove g∘f/𝒞 = f/𝒞∘g/𝒞.

2.2 Diagram🔗

In Commutative Diagram, we introduced the informal idea of a diagram as a collection of objects connected by morphisms, where a commutative diagram ensures that all paths between the same pair of objects result in the same morphism. Now, we formalize this concept.

Let 𝒞 be a category, and let 𝒮 be a small category. A diagram D of shape 𝒮 in 𝒞 is a functor D : 𝒮 → 𝒞. If 𝒮 is a thin category, then the diagram commutes, i.e., it is commutative.

Exercise: Prove that every functor preserves commutative diagrams.

Exercise: Prove that every faithful functor reflects commutative diagrams.

Let 𝒮 be a digraph. A free diagram D is a diagram of shape F(𝒮), where F(𝒮) is the free category of 𝒮. Equivalently, D can be viewed as a graph homomorphism from 𝒮 to U(𝒞), where U(𝒞) is the underlying digraph of 𝒞.

2.3 𝐒𝐞𝐭-Valued Functor🔗

A 𝐒𝐞𝐭-valued functor on 𝒞 is a functor from 𝒞 to 𝐒𝐞𝐭. Such functors map objects in 𝒞 to sets, and morphisms in 𝒞 to functions between these sets. These functors are central to many areas of category theory.

More generally, given any category 𝒟, a 𝒟-valued presheaf on 𝒞 is a functor from 𝒞op to 𝒟.

One important type of 𝐒𝐞𝐭-valued functor is the presheaf, which is a functor from 𝒞op to 𝐒𝐞𝐭. The name presheaf comes from topology, but the concept applies to arbitrary categories.

𝐒𝐞𝐭-valued functors, including presheaves, have theoretical importance due to the Yoneda Lemma, a fundamental result in category theory that will be introduced in detail in the next chapter.

A related concept is that of a concrete category, which is a category equipped with a faithful 𝐒𝐞𝐭-valued functor. A category is called concretizable iff it admits such a functor.

2.3.1 Category of Elements of a Functor🔗

Given a functor F : 𝒞 → 𝐒𝐞𝐭, the category of elements of F, denoted by S:𝒞F(S) or 𝒞F, is a category where each object represents an element s drawn from some F(S). Each such object can be viewed as a pointed set (F(S), s).

This construction allows us to study the internal structure of a 𝐒𝐞𝐭-valued functor by examining its individual elements and the relationships between them. One useful example of this is viewing a rooted forest as a presheaf on .

A rooted forest can be interpreted as a presheaf F : ℕop → 𝐒𝐞𝐭, where each set F(n) represents a layer of nodes in the forest. Specifically, F(n) consists of all nodes in the forest such that the length of the path from each node to the root is exactly n.

A rooted tree is a special case of a forest for which F(0) is a singleton set.

The following diagram shows how we can interpret opF as the free category of a forest:

[picture] el-tree.svg

Moreover, a planar forest can be viewed as a functor F : ℕop → 𝐓𝐨𝐬, where each layer is a toset.

2.3.2 Powerset Functor🔗

The powerset of a set s, denoted by 𝒫(s), is the set of all subsets of s. This concept is central to several powerset functors, which map sets to their corresponding powersets.

"code/function/P.rkt"

#lang typed/racket/base/no-check
 
(require racket/hash racket/set racket/promise)
(require "../category/Set.rkt")
 
(provide (all-defined-out))
 
(: 𝒮 𝐂𝐚𝐭)
(: dom𝒮 ( ([a : 𝒮] [b : 𝒮]) ( (→𝒮 a b) a)))
(: cod𝒮 ( ([a : 𝒮] [b : 𝒮]) ( (→𝒮 a b) b)))
(: ∘𝒮 ( ([a : 𝒮] [b : 𝒮] [c : 𝒮] ... [z : 𝒮]) ( (× (→𝒮 a b) (→𝒮 b c) ...) (→𝒮 a z))))
(: ?𝒮 (pred ( ([a : 𝒮] [b : 𝒮]) (→𝒮 a b))))
(: =𝒮 ( ([a : 𝒮] [b : 𝒮] [c : 𝒮] [d : 𝒮] ...) ( (× (→𝒮 a b) (→𝒮 c d) ...) Boolean)))
(define (𝒮 m) m)
(define-values (dom𝒮 cod𝒮 ∘𝒮 ?𝒮 =𝒮) (𝐒𝐞𝐭))
 
(: combine/key ( Any Any Any Any))
(define (combine/key k v1 v2) v1)
 
(define 𝒫
  (let ()
    (define (𝒫.map s.map)
      (for/fold ([𝒫s.map (hash s.map s.map #hash() #hash())])
                ([(v _) (in-hash s.map)])
        (define s0.map (hash-remove s.map v))
        (define 𝒫s0.map (𝒫.map s0.map))
        (hash-union 𝒫s.map 𝒫s0.map #:combine/key combine/key)))
    (λ (s)
      (define 𝒫s.map (𝒫.map (function-map s)))
      (define 𝒫s (function (lazy 𝒫s) (lazy 𝒫s) 𝒫s.map))
      𝒫s)))
 

Given a function f : a → b, where a0 is a subset of a and b0 is a subset of b, we can define several powerset functors that map a set s to 𝒫(s).

[picture] f.svg

2.3.2.1 Direct Image Functor🔗

The direct image (existential image) of a0, denoted by f(a0), is the subset of b: f(a0){f(x) | x ∈ a0}.

[picture] f_*.svg

The direct image functor (existential image functor) 𝒫 takes f to f: 𝒫(f) = f.

"code/functor/P_*.rkt"

#lang typed/racket/base/no-check
 
(require racket/hash racket/set racket/promise)
(require "../category/Set.rkt"
         "../function/P.rkt")
 
(: 𝒫_∗ ( ([a : 𝒮] [b : 𝒮]) ( (→𝒮 a b) (→𝒮 (𝒫 a) (𝒫 b)))))
(provide 𝒫_∗)
(define (𝒫_∗ f)
  (define f.map (function-map f))
  (define a (dom𝒮 f))
  (define b (cod𝒮 f))
  (define 𝒫a (𝒫 a))
  (define 𝒫b (𝒫 b))
  (define f_∗.map
    (for/hash ([(a0.map _) (in-hash (function-map 𝒫a))])
      (define b0.map
        (for/fold ([b0.map #hash()])
                  ([(x _) (in-hash a0.map)])
          (define y (hash-ref f.map x))
          (hash-set b0.map y y)))
      (values a0.map b0.map)))
  (define f_∗ (function (lazy 𝒫a) (lazy 𝒫b) f_∗.map))
  f_∗)
 
(module+ test
  (require "check.rkt")
 
  ;; Objects
  (: a 𝒮) (define a (function (lazy a) (lazy a) #hash([x0 . x0] [x1 . x1])))
  (: b 𝒮) (define b (function (lazy b) (lazy b) #hash([y0 . y0] [y1 . y1])))
  (: c 𝒮) (define c (function (lazy c) (lazy c) #hash([z0 . z0] [z1 . z1])))
 
  ;; Morphisms
  (: f (→𝒮 a b)) (define f (function (lazy a) (lazy b) #hash([x0 . y0] [x1 . y0])))
  (: g (→𝒮 b c)) (define g (function (lazy b) (lazy c) #hash([y0 . z0] [y1 . z0])))
 
  (define check-𝐒𝐞𝐭→𝐒𝐞𝐭 (check-ftr 𝐒𝐞𝐭 𝐒𝐞𝐭))
  (define check-𝒫_∗ (check-𝐒𝐞𝐭→𝐒𝐞𝐭 𝒫_∗))
  (check-𝒫_∗ a b c f g))
 
2.3.2.2 Preimage Functor🔗

The preimage (inverse image) of b0, denoted by f(b0), is the subset of a: f(b0){x ∈ a | f(x) ∈ b0}.

[picture] f^*.svg

The preimage functor (inverse image functor) 𝒫 takes f to f: 𝒫(f) = f.

"code/functor/P^*.rkt"

#lang typed/racket/base/no-check
 
(require racket/hash racket/set racket/promise)
(require "../category/Set.rkt"
         "../function/P.rkt")
 
(: 𝒫^∗ ( ([b : 𝒮] [a : 𝒮]) ( (→𝒮 a b) (→𝒮 (𝒫 b) (𝒫 a)))))
(provide 𝒫^∗)
(define (𝒫^∗ f)
  (define f.map (function-map f))
  (define a (dom𝒮 f))
  (define b (cod𝒮 f))
  (define 𝒫a (𝒫 a))
  (define 𝒫b (𝒫 b))
  (define f^∗.map
    (for/hash ([(b0.map _) (in-hash (function-map 𝒫b))])
      (define a0.map
        (for/fold ([a0.map #hash()])
                  ([(x _) (in-hash (function-map a))])
          (if (and (hash-has-key? f.map x)
                   (let ([y (hash-ref f.map x)])
                     (hash-has-key? b0.map y)))
              (hash-set a0.map x x) a0.map)))
      (values b0.map a0.map)))
  (define f^∗ (function (lazy 𝒫b) (lazy 𝒫a) f^∗.map))
  f^∗)
 
(module+ test
  (require "check.rkt" "../category/dual.rkt")
  (define  compose)
 
  ;; Objects
  (: a 𝒮) (define a (function (lazy a) (lazy a) #hash([x0 . x0] [x1 . x1])))
  (: b 𝒮) (define b (function (lazy b) (lazy b) #hash([y0 . y0] [y1 . y1])))
  (: c 𝒮) (define c (function (lazy c) (lazy c) #hash([z0 . z0] [z1 . z1])))
 
  ;; Morphisms
  (: f (→𝒮 a b)) (define f (function (lazy a) (lazy b) #hash([x0 . y0] [x1 . y0])))
  (: g (→𝒮 b c)) (define g (function (lazy b) (lazy c) #hash([y0 . z0] [y1 . z0])))
 
  (define check-𝐒𝐞𝐭†→𝐒𝐞𝐭 (check-ftr (  𝐒𝐞𝐭) 𝐒𝐞𝐭))
  (define check-𝒫^∗ (check-𝐒𝐞𝐭†→𝐒𝐞𝐭 𝒫^∗))
  (check-𝒫^∗ c b a g f))
 
2.3.2.3 Universal Image Functor🔗

The universal image of a0, denoted by f!(a0), is the subset of b: f!(a0){y ∈ b | f({y}) ⊆ a0}.

[picture] f_!.svg

The universal image functor 𝒫! takes f to f!: 𝒫!(f) = f!.

"code/functor/P_!.rkt"

#lang typed/racket/base/no-check
 
(require racket/hash racket/set racket/promise)
(require (file "P^*.rkt")
         "../category/Set.rkt"
         "../function/P.rkt")
 
(: 𝒫_! ( ([a : 𝒮] [b : 𝒮]) ( (→𝒮 a b) (→𝒮 (𝒫 a) (𝒫 b)))))
(provide 𝒫_!)
(define (𝒫_! f)
  (define f.map (function-map f))
  (define f^∗ (𝒫^∗ f))
  (define f^∗.map (function-map f^∗))
  (define a (dom𝒮 f))
  (define b (cod𝒮 f))
  (define 𝒫a (𝒫 a))
  (define 𝒫b (𝒫 b))
  (define f_!.map
    (for/hash ([(a0.map _) (in-hash (function-map 𝒫a))])
      (define b0.map
        (for/fold ([b0.map #hash()])
                  ([(y _) (in-hash (function-map b))])
          (define a1.map (hash-ref f^∗.map (hash y y)))
          (if (equal? a0.map (hash-union a0.map a1.map #:combine/key combine/key))
              (hash-set b0.map y y) b0.map)))
      (values a0.map b0.map)))
  (define f_! (function (lazy 𝒫a) (lazy 𝒫b) f_!.map))
  f_!)
 
(module+ test
  (require "check.rkt")
 
  ;; Objects
  (: a 𝒮) (define a (function (lazy a) (lazy a) #hash([x0 . x0] [x1 . x1])))
  (: b 𝒮) (define b (function (lazy b) (lazy b) #hash([y0 . y0] [y1 . y1])))
  (: c 𝒮) (define c (function (lazy c) (lazy c) #hash([z0 . z0] [z1 . z1])))
 
  ;; Morphisms
  (: f (→𝒮 a b)) (define f (function (lazy a) (lazy b) #hash([x0 . y0] [x1 . y0])))
  (: g (→𝒮 b c)) (define g (function (lazy b) (lazy c) #hash([y0 . z0] [y1 . z0])))
 
  (define check-𝐒𝐞𝐭→𝐒𝐞𝐭 (check-ftr 𝐒𝐞𝐭 𝐒𝐞𝐭))
  (define check-𝒫_! (check-𝐒𝐞𝐭→𝐒𝐞𝐭 𝒫_!))
  (check-𝒫_! a b c f g))
 
2.3.3 Hom Functor🔗

The hom functor is a key construction in category theory. It provides a way to associate each pair of objects in a category to a set of morphisms. Hom functors play a central role in studying how objects within a category are related to each other via morphisms, and they are foundational to concepts like the Yoneda Lemma.

2.3.3.1 Covariant Hom Functor🔗

The covariant hom functor Hom𝒞(a, -) : 𝒞 → 𝐒𝐞𝐭 maps a morphism j : x → y : 𝒞 to Hom𝒞(a, j).

[picture] Hom_1.svg

(: 𝒞 𝐂𝐚𝐭) (: a 𝒞)
(: |(→𝒞 a _)| ( ([x : 𝒞] [y : 𝒞]) ( (→𝒞 x y) ( (→𝒞 a x) (→𝒞 a y)))))
(define |(→𝒞 a _)| (curry ∘𝒞))

Exercise: Prove Hom𝒞(a, idx) = idHom𝒞(a, x).

Exercise: Prove Hom𝒞(a, j)∘Hom𝒞(a, g) = Hom𝒞(a, j∘g).

2.3.3.2 Contravariant Hom Functor🔗

The contravariant hom functor Hom𝒞(-, x) : 𝒞op → 𝐒𝐞𝐭 maps a morphism i : b → a : 𝒞 to Hom𝒞(i, x).

[picture] Hom_2.svg

(: 𝒞 𝐂𝐚𝐭) (: x 𝒞)
(: |(→𝒞 _ x)| ( ([a : 𝒞] [b : 𝒞]) ( (→𝒞 b a) ( (→𝒞 a x) (→𝒞 b x)))))
(define |(→𝒞 _ x)| (curryr ∘𝒞))

Exercise: Prove Hom𝒞(ida, x) = idHom𝒞(a, x).

Exercise: Prove Hom𝒞(i, x)∘Hom𝒞(f, x) = Hom𝒞(f∘i, x).

2.3.3.3 Two-Variable Hom Functor🔗

The two-variable hom functor Hom𝒞(-, -) : 𝒞op×𝒞 → 𝐒𝐞𝐭 maps (i, j) : (b, x)(a, y) : 𝒞×𝒞 to Hom𝒞(i, j).

[picture] Hom_3.svg

(: 𝒞 𝐂𝐚𝐭)
(: |(→𝒞 _ _)| ( ([a : 𝒞] [b : 𝒞] [x : 𝒞] [y : 𝒞]) ( (× (→𝒞 b a) (→𝒞 x y)) ( (→𝒞 a x) (→𝒞 b y)))))
(define (|(→𝒞 _ _)| i j)
  (define |(→𝒞 i j)| (λ (f) (∘𝒞 j f i)))
  |(→𝒞 i j)|)

Exercise: Prove Hom𝒞(ida, idx) = idHom𝒞(a, x).

Exercise: Prove the interchange law: Hom𝒞(i, j)∘Hom𝒞(f, g) = Hom𝒞(f∘i, j∘g).

2.3.4 Cayley’s Theorem🔗

In the context of category theory, Cayley’s Theorem asserts that every small category 𝒞 is isomorphic to a subcategory 𝒮 of 𝐒𝐞𝐭. This subcategory 𝒮 is known as the Cayley’s representation of 𝒞.

To demonstrate this, we define isomorphisms H : 𝒞 → 𝒮 and G : 𝒮 → 𝒞:

Note that the definition of G is pseudocode. Because sets and functions are treated as procedures here, we cannot define dom𝒮 and choose-id.

(: 𝒞 𝐂𝐚𝐭) (: 𝒮 𝐂𝐚𝐭)
 
(: H ( ([b : 𝒞] [c : 𝒞]) ( (→𝒞 b c) ( ([a : 𝒞]) ( (→𝒞 a b) (→𝒞 a c))))))
(define H (curry ∘𝒞))
 
(: G ( ([b : 𝒞] [c : 𝒞]) ( ( ([a : 𝒞]) ( (→𝒞 a b) (→𝒞 a c))) (→𝒞 b c))))
(define (G Hg)
  (define Hb (dom𝒮 Hg))
  (define b (choose-id Hb))
  (define g (Hg b))
  g)

Exercise: Prove H = G1 and G = H1.

Exercise: Think about the relationships between H and covariant hom functor.

H is equal to the composite of the slice functor 𝒞/- and the forgetful functor U : 𝐂𝐚𝐭 → 𝒮:

[picture] H_1.svg

(: U ( ([b : 𝒞] [c : 𝒞]) ( (→𝐂𝐚𝐭 𝒞/b 𝒞/c) (→𝒮 (H b) (H c)))))
(define (U 𝒞/g)
  (: Hg ( ([a : 𝒞]) ( (→𝒞 a b) (→𝒞 a c))))
  (define (Hg f)
    (define b (cod𝒞 f))
    (define g (caar (𝒞/g `((,b) (,b ,b)))))
    (∘𝒞 g f))
  Hg)

Exercise: Prove H = U∘𝒞/-.

Next, we apply Cayley’s representation to the opposite category 𝒞op:

(: 𝒞 𝐂𝐚𝐭) (: 𝒮 𝐂𝐚𝐭)
 
(: H ( ([b : 𝒞] [a : 𝒞]) ( (→𝒞 a b) ( ([c : 𝒞]) ( (→𝒞 b c) (→𝒞 a c))))))
(define H (curryr ∘𝒞))
 
(: G ( ([b : 𝒞] [a : 𝒞]) ( ( ([c : 𝒞]) ( (→𝒞 b c) (→𝒞 a c))) (→𝒞 a b))))
(define (G Hf)
  (define Hb (dom𝒮 Hf))
  (define b (choose-id Hb))
  (define f (Hf b))
  f)

Exercise: Prove H = G1 and G = H1.

Exercise: Think about the relationships between H and contravariant hom functor.

H is equal to the composite of the coslice functor -/𝒞 and the forgetful functor U : 𝐂𝐚𝐭 → 𝒮:

[picture] H_2.svg

(: U ( ([b : 𝒞] [a : 𝒞]) ( (→𝐂𝐚𝐭 b/𝒞 a/𝒞) (→𝒮 (H b) (H a)))))
(define (U f/𝒞)
  (: Hf ( ([c : 𝒞]) ( (→𝒞 b c) (→𝒞 a c))))
  (define (Hf g)
    (define b (dom𝒞 g))
    (define f (caadr (f/𝒞 `((,b ,b) (,b)))))
    (∘𝒞 g f))
  Hf)

Exercise: Prove H = U∘-/𝒞.

2.3.5 Action🔗

In this context, actions are assumed to be left actions by default. There is also a concept of right action, which is a function β : S×B → S.

An action of a set A on a set S is a function α : A×S → S. α shows how each element of A transforms the elements of S in a consistent manner.

If M is a group, then α is a group action.

Let M be a monoid (M, ∘, 1), a monoid action of M on a set S is an action α : M×S → S satisfying the following properties:

  1. Identity: ∀s ∈ S, α(1, s) = s.

  2. Associativity: ∀m, n ∈ M, ∀s ∈ S, α(m∘n, s) = α(m, α(n, s)).

These properties ensure that the monoid action respects the structure of the monoid, providing a coherent way to apply the elements of M to the elements of S.

Another common way to denote a monoid action is by writing ms to represent α(m, s). Using this notation, the properties of a monoid action can be restated as follows:

  1. Identity: ∀s ∈ S, 1s = s.

  2. Associativity: ∀m, n ∈ M, ∀s ∈ S, (m∘n)s = m(ns).

In this notation, the monoid action is described more compactly, emphasizing the direct application of elements from the monoid M to the set S. S is also called an M-set in this way.

2.3.5.1 Monoid Actions as Functors🔗

To further explore the connection between monoid actions and functors, we can curry α and obtain a function Fα1: M → [S → S], where Fα1(m)(s) = α(m, s) = ms. Next, we can view M as an OOC C(M), and then define a 𝐒𝐞𝐭-valued functor Fα: C(M) → 𝐒𝐞𝐭, where Fα() = S and Fα(m) = Fα1(m) = α(m, -).

[picture] act.svg

Exercise: Prove that Fα1 is a monoid homomorphism.

In this way, we can define an action of the monoid M on the object S in the category 𝒞 to be a functor ρ : C(M) → 𝒞, where ρ() = S.

A category action of the category 𝒞 in the category 𝒟 is just a functor from 𝒞 to 𝒟.

2.3.5.2 𝐒𝐞𝐭-Valued Functors as Typed Actions🔗

We’ve shown that monoid actions can be viewed as functors from an OOC to 𝐒𝐞𝐭. Can we view any 𝐒𝐞𝐭-valued functor as an action that generalizes monoid actions? Indeed, we can! Such an action is called a typed action.

A typed action of M on S involves a type set T and a function type : S → T. This can be seen as S being divided into subsets according to their types, and the elements of M acting on these subsets.

For a functor F : 𝒞 → 𝐒𝐞𝐭, we can view it as a typed action in this way: T = 𝒞0, M = 𝒞1, and S = ∐t∈TF(t), where F(t) = {s ∈ S | type(s) = t}.

[picture] typed-act.svg

2.4 Finite Automaton🔗

A finite automaton (FA) is a powerful mathematical model used to represent and analyze systems that transition between discrete states based on inputs. This model plays a critical role in fields such as language processing, control systems, and computational theory. By abstracting systems as a finite number of states and transitions, FAs provide a framework for designing algorithms and predicting system behavior.

2.4.1 Deterministic Finite Automaton🔗

A deterministic finite automaton (DFA) is a specific type of FA with well-defined, predictable behavior, where each input uniquely determines the next state. can be represented as a state diagram, a state table, or as a tuple (A, S, s0, φ):

φ : A×S → S takes an input letter and the current state as arguments and returns the next state. By currying φ, we can view any element in A as a transition. Similarly, we’d like to find a way to represent a sequence of transitions. We define the function φ*: A*×S → S:

  1. ∀s ∈ S, φ*((), s) = s

  2. ∀s ∈ S, ∀w ∈ A*, ∀a ∈ A, φ*((a)w, s) = φ(a, φ*(w, s))

Exercise: Prove that φ* is a monoid action of A* on S in 𝐒𝐞𝐭.

In addition to the monoid action φ*, a DFA often employ a run function ρ : A* → S, which takes a sequence from A* and returns a final state of after processing the entire sequence, starting from the start state s0: ∀w ∈ A*, ρ(w) = φ*(w, s0).

Here is a Racket example for the DFA 1 (A1 = {x, y}, S1 = {s1, b1}, s1, φ1), which expects the last letter to be not y (s means "start" and b means "bad"):

[picture] M_1.svg

S1 \ A1

 

x

 

y

s1

 

s1

 

b1

b1

 

s1

 

b1

"code/functor/DFA.rkt"

#lang typed/racket/base/no-check
 
(require racket/match racket/function)
(provide ℳ1 A1 S1 s1 φ1 φ1* ρ1 Fφ1*)
 
(module+ test (require rackunit))
 
(: #;𝐒𝐞𝐭  𝒮 𝐂𝐚𝐭) (define (𝒮 m) m) (define ∘𝒮 compose)
(: #;𝐋𝐢𝐬𝐭  𝐂𝐚𝐭) (define ( m) m) (define ∘ℒ append)
 
(define-type A1 ( #\x #\y))
(define-type S1 ( 's1 'b1))
 
(: ℳ1 (Listof (Immutable-Vector A1 S1 S1)))
(define ℳ1
  '(#[#\nul s1 s1]
    #[#\nul b1 b1]
 
    #[#\x s1 s1]
    #[#\x b1 s1]
 
    #[#\y s1 b1]
    #[#\y b1 b1]))
 
(: s1 S1) (define s1 's1)
(: φ1 ( (× A1 S1) S1))
(define (φ1 a s)
  (or
   (for/or ([i (in-list ℳ1)])
     (match i
       [`#[,(? (curry eq? a))
           ,(? (curry eq? s))
           ,t]
        t]
       [_ #f]))
   (raise-arguments-error
    'φ1 "invalid character or state"
    "character" a
    "state" s)))
 
(define-type A1* (Listof A1))
(: φ1* ( (× A1* S1) S1))
(define (φ1* a* s)
  (match a*
    ['() s]
    [`(,a . ,w) (φ1 a (φ1* w s))]))
 
(: ρ1 ( A1* S1))
(define (ρ1 a*) (φ1* a* s1))
 
(module+ test
  (check-eq? (ρ1 '()) s1)
 
  (check-eq? (ρ1 '(#\y #\y)) 'b1)
  (check-eq? (ρ1 '(#\y #\x)) 'b1)
  (check-eq? (ρ1 '(#\x #\y)) 's1)
  (check-eq? (ρ1 '(#\x #\x)) 's1))
 
(define-type  Null)
(: Fφ1* ( #;A* (→ℒ  ) (→𝒮 S1 S1)))
(define Fφ1* (curry φ1*))
 
(module+ test
  (define str* '("x" "y" "xx" "xy" "yx" "yy"))
  (for* ([i (in-list str*)]
         [j (in-list str*)])
    (define m (reverse (string->list i)))
    (define n (reverse (string->list j)))
    (check-eq? ((Fφ1* (∘ℒ n m)) s1)
               ((∘𝒮 (Fφ1* n) (Fφ1* m)) s1))))
 

Exercise: Try to implement another Racket example for the DFA 2 (A2 = {x, y}, S2 = {s2, b2, o2}, s2, φ2), which expects the first letter to be x (o means "ok"):

[picture] M_2.svg

S2 \ A2

 

x

 

y

s2

 

o2

 

b2

b2

 

b2

 

b2

o2

 

o2

 

o2

2.4.2 Typed Deterministic Finite Automaton🔗

DFAs are typically characterized by their complete state tables, meaning that for every state and every input letter, there is a defined transition to a state.

In a typed deterministic finite automaton (TDFA), its state table does not need to be complete. Instead, its alphabet and states are typed, meaning that only certain transitions are valid. This introduces a layer of flexibility and specificity in modeling state transitions, where not every state needs to handle every possible input letter. In some contexts, certain states might only handle a subset of the alphabet, and any undefined transition might signify an error or a special condition that needs separate handling.

A DFA can be represented by a tuple (A, S, s0, φ). In contrast, a TDFA can be represented by a tuple (𝒢, S, s0, φ), where:

We can use a state diagram and a state table to illustrate a TDFA , which can be viewed as a combination of multiple DFAs 1 and 2 (q means "quit", a means "accept", and r means "reject"):

[picture] M.svg

S \ 𝒢1

 

1

 

2

 

q

 

x

 

y

s0

 

s1

 

s2

 

 

 

a0

 

s1

 

s2

 

 

 

r0

 

s1

 

s2

 

 

 

s1

 

 

 

a0

 

s1

 

b1

b1

 

 

 

r0

 

s1

 

b1

s2

 

 

 

r0

 

o2

 

b2

b2

 

 

 

r0

 

b2

 

b2

o2

 

 

 

a0

 

o2

 

o2

Let S0 = {s0, a0, r0}, S1 = {s1, b1} and S2 = {s2, b2, o2}. In the typed alphabet 𝒢, S1 and S2 are the state spaces of 1 and 2 respectively:

[picture] G.svg

Similar to DFAs, φ : 𝒢 → 𝐒𝐞𝐭 generates a typed action φ* : F(𝒢) → 𝐒𝐞𝐭. F(𝒢) is the free category of 𝒢.

Exercise: Prove that F(𝒢)φ* is isomorphic to the free category of the state diagram of .

Here is how to implement in Racket:

"code/functor/TDFA.rkt"

#lang typed/racket/base/no-check
 
(require racket/match racket/function racket/set)
(require "DFA.rkt"
         "../../exercises/functor/DFA.rkt"
         "../../exercises/functor/F.rkt"
         "../../exercises/functor/make-path.rkt")
(provide  𝒢0 𝒢1 𝒢 F𝒢 S s φ φ* ρ)
 
(module+ test (require rackunit))
 
(: #;𝐒𝐞𝐭  𝒮 𝐂𝐚𝐭) (define (𝒮 m) m) (define ∘𝒮 compose)
(: #;𝐋𝐢𝐬𝐭  𝐂𝐚𝐭) (define ( m) m) (define ∘ℒ append)
 
(define-type S0 ( 's0 'a0 'r0))
(define-type S  ( S0 S1 S2))
(define-type 𝒢0 S)
(define-type 𝒢1 (Immutable-Vector Char 𝒢0 𝒢0))
 
(:  (Listof 𝒢1))
(define 
  (append
   '(#[#\nul s0 s0]
     #[#\nul a0 a0]
     #[#\nul r0 r0]
 
     #[#\1 s0 s1]
     #[#\1 a0 s1]
     #[#\1 r0 s1]
 
     #[#\q s1 a0]
     #[#\q b1 r0]
 
     #[#\2 s0 s2]
     #[#\2 a0 s2]
     #[#\2 r0 s2]
 
     #[#\q s2 a0]
     #[#\q b2 r0]
     #[#\q o2 a0])
   ℳ1 ℳ2))
 
(: 𝒢 (Immutable-Vectorof 𝒢1))
(define 𝒢
  '#(#[#\nul S0 S0]
     #[#\nul S1 S1]
     #[#\nul S2 S2]
 
     #[#\1 S0 S1]
     #[#\2 S0 S2]
 
     #[#\q S1 S0]
     #[#\q S2 S0]
 
     #[#\x S1 S1]
     #[#\x S2 S2]
 
     #[#\y S1 S1]
     #[#\y S2 S2]))
 
(: s 𝒢0) (define s 's0)
(: φ (case→ ( 𝒢0 𝐒𝐞𝐭) ( 𝒢1 →𝐒𝐞𝐭)))
(define (φ g)
  (match g
    [(? symbol?) (∘𝒮)]
    [`#[,a ,n0 ,n1]
     #:when
     (and (char? a)
          (symbol? n0)
          (symbol? n1))
     (λ (s)
       (or
        (for/or ([i : 𝒢1 (in-list )])
          (match i
            [`#[,(? (curry eq? a))
                ,(? (curry eq? s))
                ,t]
             t]
            [_ #f]))
        (raise-arguments-error
         'φ "invalid character or state"
         "character" a
         "state" s)))]))
 
(define F𝒢 (F 𝒢))
(module+ test
  (require "../category/check.rkt")
 
  ;; Objects
  (define S0 (make-path 𝒢 'S0 ""))
  (define S1 (make-path 𝒢 'S1 ""))
  (define S2 (make-path 𝒢 'S2 ""))
  (define S3 (make-path 𝒢 'S3 ""))
 
  ;; Morphisms
  (define f (make-path 𝒢 'S0 "1yxy"))
  (define g (make-path 𝒢 'S1 "xq2y"))
  (define h (make-path 𝒢 'S2 "xxyq"))
 
  (define check-F𝒢 (check-cat F𝒢))
  (check-F𝒢 S0 S1 S2 S3 f g h))
 
(: φ* ( ([a : F𝒢] [b : F𝒢]) ( (→F𝒢 a b) (→𝐒𝐞𝐭 (φ* a) (φ* b)))))
(define (φ* g*) (apply ∘𝒮 (map φ g*)))
 
(: ρ ( →F𝒢 S))
(define (ρ g*) ((φ* g*) s))
 
(module+ test
  (check-eq? (ρ S0) s)
  (check-eq? ((φ* (∘ℒ g f)) s)
             ((∘𝒮 (φ* g) (φ* f)) s)))
 
2.4.3 Nondeterministic Finite Automaton🔗

A nondeterministic finite automaton (NFA) provides greater flexibility than a DFA by allowing each cell in its state table to contain a set of possible states. Unlike in a DFA or TDFA, where a given state and letter determine a unique next state (or none at all), an NFA allows multiple possible states for the same input.

From a category theory perspective, the key difference between DFAs and NFAs lies in representing φ as a transition relation rather than a function. This distinction means φ* operates as a monoid action in 𝐑𝐞𝐥 rather than in 𝐒𝐞𝐭.

For an NFA, the transition relation φ can be represented as a function with a codomain of powersets (i.e., φ : A×S → 𝒫(S)). This functional view works because we focus on the set of all possible next states for a given state and letter.

Using John McCarthy’s amb operator, we can represent this function as a procedure that returns multiple ambiguous results, capturing all possible next states for each input. This aligns with the nondeterministic nature of NFAs, where each input may lead to several potential states.

The following is a Racket example for the NFA 3 (A3 = {x, y}, S3 = {s3, b3, o3}, s3, φ3), which expects a sequence like x...xy...y.

[picture] M_3.svg

S3 \ A3

 

x

 

y

s3

 

{s3, b3}

 

{}

b3

 

{}

 

{o3}

o3

 

{}

 

{o3}

"code/functor/NFA.rkt"

#lang typed/racket/base/no-check
 
(require racket/function racket/match racket/set amb)
(provide ℳ3 A3 S3 s3 φ3 φ3* ρ3 Fφ3*)
 
(module+ test (require rackunit))
 
(: #;𝐑𝐞𝐥   𝐂𝐚𝐭) (define ( m) m) (define ∘ℛ compose)
(: #;𝐋𝐢𝐬𝐭  𝐂𝐚𝐭) (define ( m) m) (define ∘ℒ append)
 
(define-type A3 ( #\x #\y))
(define-type S3 ( 's3 'b3 'o3))
 
(: ℳ3 (Listof (Immutable-Vector A3 S3 S3)))
(define ℳ3
  '(#[#\nul s3 s3]
    #[#\nul b3 b3]
    #[#\nul o3 o3]
 
    #[#\x s3 s3]
    #[#\x s3 b3]
 
    #[#\y b3 o3]
    #[#\y o3 o3]))
 
(: s3 S3) (define s3 's3)
(: φ3 ( (× A3 S3) S3))
(define (φ3 a s)
  (for/amb ([i (in-list ℳ3)])
    (match i
      [`#[,(? (curry eq? a))
          ,(? (curry eq? s))
          ,t]
       t]
      [_ (amb)])))
 
(define-type A3* (Listof A3))
(: φ3* ( (× A3* S3) S3))
(define (φ3* a* s)
  (match a*
    ['() s]
    [`(,a . ,w) (φ3 a (φ3* w s))]))
 
(: ρ3 ( A3* (𝒫 S3)))
(define (ρ3 a*) (for/set ([s (in-amb (φ3* a* s3))]) s))
 
(module+ test
  (check-equal? (ρ3 '()) (set s3))
 
  (check-equal? (ρ3 '(#\x #\x #\y)) (set))
  (check-equal? (ρ3 '(#\y #\y #\x)) (set 'o3))
  (check-equal? (ρ3 '(#\y #\x #\x)) (set 'o3))
  (check-equal? (ρ3 '(#\x #\x #\x)) (set 's3 'b3)))
 
(define-type  Null)
(: Fφ3* ( #;A* (→ℒ  ) (→ℛ S3 S3)))
(define Fφ3* (curry φ3*))
 
(module+ test
  (define str* '("x" "y" "xx" "xy" "yx" "yy"))
  (for* ([i (in-list str*)]
         [j (in-list str*)])
    (define m (reverse (string->list i)))
    (define n (reverse (string->list j)))
    (check-equal?
     (for/set ([s (in-amb ((Fφ3* (∘ℒ n m)) s3))]) s)
     (for/set ([s (in-amb ((∘ℛ (Fφ3* n) (Fφ3* m)) s3))]) s))))