Let's explore Lisp's finegrained type hierarchy!
We begin with a shallow comparison to Haskell, a rapid tour of type theory, try in vain to defend dynamic approaches, give a somewhat humorous account of history, note that you've been bamboozled —type's have always been there—, then go into technical details of some Lisp types, and finally conclude by showing how macros permit typing.
Goals for this article:
Pair
and Maybe
can be defined in Lisp.
Including heterogeneously typed lists!Unless suggested otherwise, the phrase “Lisp” refers to Common Lisp as supported by Emacs Lisp. As such, the resulting discussion is applicable to a number of Lisp dialects —I'm ignoring editing types such as buffers and keymaps, for now.
( Original print by Baneen Alhassy as a birthday present to me. )
I have convinced a number of my peers to use Emacs/Spacemacs/Doomemacs, but my efforts to get them to even consider trying Lisp have been met with staunch rejection. These peers are familiar with Haskell, and almost all know Agda, so you'd think they'd be willing to try Lisp —it's there, part of their editor— but the superficial chasm in terms of syntax and types is more than enough apparently. In this article, I aim to explore the type system of (Emacs) Lisp and occasionally make comparisons to Haskell. Perhaps in the end some of my Haskell peers would be willing to try it out.
↯ I almost never use Haskell for any daytoday dealings.
✓ The ideas expressed by its community are why I try to keep updated on the language.
↯ No one around me knows anything about Lisp, but they dislike it due to the parens.
✓ I love it and use it for Emacs configuration and recently to prototype my PhD research.
(งಠ_ಠ)ง
Lately, in Lisp, I'll write a nested loop (gasp!) then, for fun, try to make it a oneliner! Sometimes, I actually think the loop formulation is clearer and I leave it as a loop —Breaking news: Two Haskell readers just died.
What I like and why:
Haskell  ⇒  Executable category theory; compact & eloquent 
Lisp  ⇒  Extensible language; malleable, uniform, beautiful 
Documentation?
Haskell  ⇒  Hoogle; can search by type alone! 
Emacs Lisp  ⇒  Selfdocumenting; Mx apropos 
How has using the language affected me?
Haskell  I almost always think interms compoistionality, functors, & currying 
Lisp  Documentation strings, units tests, and metaprogramming are second nature 
It may not be entirely accurate to say that Lisp's type system is more expressive than Haskell's as it's orthogonal in many respects; although it is closer to that of Liquid Haskell.
Types allow us to treat objects according a similar structure or interface. Unlike Haskell and other statically typed systems, in Lisp we have that types can overlap. As such, here's our working definition.
A type is a collection of possible objects.
To say “\(e\) has type \(τ\)” one writes \(e : τ\), or in Lisp: (typep e 'τ)
.
Haskellers and others may append to this definition the following, which we will not bother with: Type membership is determined by inspecting syntactic structure and so is decidable.
✓ Typing is one of the simplest forms of “assertioncomments”: Documenting a property of your code in a way that the machine can verify.
If you're gonna comment on what kind of thing you're working with, why not have the comment checked by the machine.
Common types  integer, number, string, keyword, array, cons, list, vector, macro, function, atom 
Top  t has everything as an element 
Unit  null has one element named nil 
Bottom  nil has no elements at all 
Union  (or τ₀ τ₁ … τₙ) has elements any element in any type τᵢ 
Intersection  (and τ₀ τ₁ … τₙ) has elements that are in all the types τᵢ 
Complement  (not τ) has elements that are not of type τ 
Enumeration  (member x₀ … xₙ) is the type consisting of only the elements xᵢ 
Singleton  (eql x) is the type with only the element x 
Comprehension  (satisfies p) is the type of values that satisfy predicate p 
Let's see some examples:
;; The universal type “t”, has everything as its value. (typep 'x 't) ;; ⇒ true (typep 12 't) ;; ⇒ true ;; The empty type: nil (typep 'x 'nil) ;; ⇒ false; nil has no values. ;; The type “null” contains the one value “nil”. (typep nil 'null) ;; ⇒ true (typep () 'null) ;; ⇒ true ;; “(eql x)” is the singelton type consisting of only x. (typep 3 '(eql 3)) ;; ⇒ true (typep 4 '(eql 3)) ;; ⇒ false ;; “(member x₀ … xₙ)” denotes the enumerated type consisting of only the xᵢ. (typep 3 '(member 3 x "c")) ;; ⇒ true (typep 'x '(member 3 x "c")) ;; ⇒ true (typep 'y '(member 3 x "c")) ;; ⇒ false ;; “(satisfies p)” is the type of values that satisfy predicate p. (typep 12 '(satisfies (lambda (x) (oddp x)))) ;; ⇒ false (typep 12 '(satisfies evenp) ) ;; ⇒ true ;; Computation rule for comprehension types. ;; (typep x '(satisfies p)) ≈ (if (p x) t nil)
Here's a convenient one: (booleanp x) ≈ (typep x '(member t nil))
.
(booleanp 2) ;; ⇒ false (booleanp nil) ;; ⇒ true
Strongly typed languages like Haskell allow only a number of the type formers listed
above. For example, Haskell does not allow unions but instead offers socalled sum
types. Moreover, unlike Haskell, Lisp is nonparametric:
We may pick a branch of computation according to the type of a value.
Such case analysis is available in languages such as C# —c.f.,
is is as or is as is. Finally, it is important to realise that cons
is a monomorphic
type
—it just means an (arbitrary) element consisting of two parts called car
and cdr
—
we show how to form a polymorphic product type below.
We may ask for the ‘primitive type’ of an object;
which is the simplest builtin type that it belongs to,
such as integer, string, cons, symbol, record, subr, and a few others.
As such, Lisp objects come with an intrinsic primitive type;
e.g., '(1 "2" 'three)
is a list and can only be treated as a value of
another type if an explicit coercion is used.
In Lisp, rather than variables, it is values that are associated with a type.
One may optionally declare the types of variables, like in OCaml.
Lisp (primitive) types are inferred!
“Values have types, not variables.” —Paul Graham, ANSI Common Lisp
Let's review some important features of type systems and how they manifest themselves in Lisp.
The typing relationship “:” is usually deterministic in its second argument for
static languages: e : τ ∧ e : τ′ ⇒ τ ≈ τ′
. However this is not the case with
Lisp's typep
.
Style  Definition  Examples 

Static  Variables have a fixed type; compile time  Haskell & C# 
Dynamic  Values have a fixed type; runtime  Lisp & Smalltalk 
In some sense, dynamic languages make it easy to produce polymorphic functions. Ironically, the previous sentences is only meaningful if you acknowledge the importance of types and type variables.
In Lisp, types are inferred and needn't be declared. However, the declaration serves as a nice documentation to further readers ;)
(setq ellew 314) (typeof ellew) ;; ⇒ integer (setq ellew "oh my") (typeof ellew) ;; ⇒ string
typeof
function returns the type of a given object.
We may check the type of an item using typep
, whose second argument
is a “type specifiers”
—an expressions whose value denotes a type; e.g., the or
expression below
forms a ‘union type’.
There's also checktype
: It's like typep
but instead of yielding true or
false, it stays quiet in the former and signals a type error in the latter.
(checktype 12 integer) ;; ⇒ nil, i.e., no error (checktype 12 (or symbol integer)) ;; nil; i.e., no error (checktype "12" (or symbol integer)) ;; Crash: Type error!
In summary:
(equal τ (typeof e)) 
≈ 
(typep e τ) 
(checktype e τ) 
≈ 
(unless (typep e 'τ) (error "⋯")) 
( Note: (unless x y) ≈ (when (not x) y)
.)
Types are the central organising principle of the theory of programming languages. Language features are manifestations of type structure. The syntax of a language is governed by the constructs that define its types, and its semantics is determined by the interactions among those constructs.
— Robert Harper, Practical Foundations for Programming Languages
Besides atoms like numbers and strings, the only way to form new terms in Lisp is using “modus ponens”, or “function application”. Here's a first approximation:
f : τ₁ → ⋯ → τₙ → τ e₁ : τ₁ … eₙ : τₙ  (f e₁ … eₙ) : τ
One reads such a fraction as follows: If each part of the numerator —the ‘hypothesises’— is true, then so is the denominator —the ‘conclusion’.
An abstract syntax tree, or ‘AST’, is a tree with operators for branches and arguments for children. A tree is of kind τ if the topmost branching operator has τ as its resulting type. Here's an improved rule:
f : τ₁ → ⋯ → τₙ → τ e₁ : AST τ₁ … eₙ : AST τₙ  (f e₁ … eₙ) : AST τ
A Lisp toplevel then may execute or interpret such a form to obtain a value:
When we write e
at a toplevel, it is essentially (eval e)
that is invoked.
e : AST τ  (eval e) : τ
However, we may also protect against evaluation.
e : AST τ  (quote e) : AST τ
We have the following execution rules, where ‘⟿’ denotes “reduces to”.
(eval a) ⟿ a ;; for atom ‘a’ (eval (quote e)) ⟿ e (eval (f e₁ … eₙ)) ⟿ (f (eval e₁) ⋯ (eval eₙ)) ;; Actually invoke ‘f’
A conceptual model of Lisp is eval
.
There's also the matter of “scope”, or ‘life time’, of a variable.
Style  Definition  Examples 

Lexical  … only in visible code  Nearly every language! 
Dynamic  … every place imaginable  Bash, Perl, & allowable in some Lisps 
That is, dynamic scope means a local variable not only acts as a global variable for the rest of the scope but it does so even in the definitions of predefined methods being invoked in the scope.
(setq it "bye") (defun go () it) (let ((it 3)) (go)) ;; ⇒ 3; even though “it” does not occur textually! ;; Temporarily enable lexical binding in Emacs Lisp (setq lexicalbinding t) (let ((it 3)) (go)) ;; ⇒ bye; as most languages would act
Dynamic scope lets bindings leak down into all constituents in its wake.
That is fantastic when we want to do unit tests involving utilities with sideeffects: We simply locally redefine the sideeffect component to, say, do nothing. (─‿‿─)
Style  Definition  Examples 

Strong  Almost never  Lisp & Haskell 
Weak  Try as best as possible  JavaScript & C 
Strong systems will not accidentally coerce terms.
Lisp has a coerce form; but coercion semantics is generally unsound in any language and so should be used with tremendous caution. ( Though Haskell has some sensible coercions as well as unsafe one. )
e : α  (coerce e β) : β
We have a magical way to turn elements of type α to elements of type β. Some languages call this type casting.
Here's a cute example.
(coerce '(76 105 115 112) 'string) ;; ⇒ Lisp
We may perform type annotations using the form the
; e.g.,
the Haskell expression (1 :: Int) + 2
checks the type annotation,
and, if it passes, yields the value and the expression is computed.
Likewise, (the type name)
yields name
provided it has type type
.
(+ (the integer 1) (the integer 2)) ;; ⇒ 3 (+ (the integer 1) (the integer "2")) ;; ⇒ Type error.
Computationally, using or
as a control structure for lazy sequencing with leftunit nil
:
(the τ e) ≈ (or (checktype e τ) e) 
Sometimes a value can be one of several types. This is specified using union types; nested unions are essentially flattened —which is a property of ‘or’, as we shall come to see.
(typep 12 'integer) ;; ⇒ t (typep 'a 'symbol) ;; ⇒ t (setq woah 12) (typep woah '(or integer symbol)) ;; ⇒ t (setq woah 'nice) (typep woah '(or integer symbol)) ;; ⇒ t
When given a union type, we may want to compute according to the type of a value.
typecase
.nil
when no case fits; use etypecase
to have an error instead of nil
.(typecase woah (integer (+1 woah)) (symbol 'cool) (t "yikes"))
Types are not objects in Common Lisp. There is no object that corresponds to the type
integer
, for example. What we get from a function liketypeof
, and give as an argument to a function liketypep
, is not a type, but a type specifier. A type specifier is the name of a type. —Paul Graham, ANSI Common Lisp
Type specifiers are essentially transformed into predicates as follows.
(typep x 'τ) ≈ (τp x) ;; E.g., τ ≈ integer (typep x '(and τ₁ … τₙ)) ≈ (and (typep x τ₁) … (typep x τₙ)) (typep x '(or τ₁ … τₙ)) ≈ (or (typep x τ₁) … (typep x τₙ)) (typep x '(not τ)) ≈ (not (typep x τ)) (typep x '(member e₁ … eₙ)) ≈ (or (eql x e₁) … (eql x eₙ)) (typep x '(satisfies p)) ≈ (p x)
Type specifiers are thus essentially ‘characteristic functions’ from mathematics.
deftype
If we use a type specifier often, we may wish to abbreviate it using
the deftype macro —it is like defmacro
but expands into a type specifier
instead of an expression.
We can define new types that will then work with typecase
and friends
as follows:
mytypep
.Register it using deftype.
You could just do number 3 directly, but it's useful to have the predicate form of a type descriptor.
For example, here's the three steps for a type of lists of numbers drawn from (∞..9]
.
;; Make the predicate (defun smallnumberseqp (thing) (and (sequencep thing) (every #'numberp thing) (every (lambda (x) (< x 10)) thing))) ;; Test it (setq yes '(1 2 4)) (setq no '(1 20 4)) (smallnumberseqp yes) ;; ⇒ t ;; Register it (deftype smallnumberseq () '(satisfies smallnumberseqp)) ;; Use it (typep yes 'smallnumberseq) ;; ⇒ true (typep no 'smallnumberseq) ;; ⇒ false
Arguments are processed the same as for defmacro
except that optional
arguments without explicit defaults use *
instead of nil
as the default value.
From the deftype docs, here are some examples:
(cldeftype null () '(satisfies null)) ; predefined (cldeftype list () '(or null cons)) ; predefined (cldeftype unsignedbyte (&optional bits) (list 'integer 0 (if (eq bits '*) bits (1 (lsh 1 bits))))) ;; Some equivalences (unsignedbyte 8) ≡ (integer 0 255) (unsignedbyte) ≡ (integer 0 *) unsignedbyte ≡ (integer 0 *)
null
is the
predicate that checks if a list is empty yet null
is the type specifying such lists.
Let's form a type of pairs directly —which is not ideal!
This is a polymorphic datatype: It takes two type arguments, called a
and b
below.
(deftype pair (a b &optional type) `(satisfies (lambda (x) (and (consp x) (typep (car x) (quote ,a)) (typep (cdr x) (quote ,b)))))) (typep '("x" . 2) '(pair string integer)) ;; ⇒ true (typep '("x" . 2) '(pair symbol integer)) ;; ⇒ false (typep nil '(pair integer integer)) ;; ⇒ false (typep 23 '(pair integer integer)) ;; ⇒ false (setq ss "nice" nn 114) (typep `(,ss . ,nn) '(pair string integer)) ;; ⇒ true (typep (cons ss nn) '(pair string integer)) ;; ⇒ true ;; The following are false since ss and nn are quoted symbols! (typep '(ss . nn) '(pair string integer)) ;; ⇒ false (typep `(cons ss nn) '(pair string integer)) ;; ⇒ false
Exercise: Define the polymorphic maybe
type
such that (maybe τ)
has elements being either nil
or a value of τ
.
Let's define type listof
such that (listof τ)
is the type of lists
whose elements are all values of type τ
.
;; Make the predicate (defun listofp (τ thing) (and (listp thing) (every (lambda (x) (typep x τ)) thing))) ;; Test it (listofp 'integer '(1 2 3)) ;; ⇒ true (listofp 'integer '(1 two 3)) ;; ⇒ false (listofp 'string '()) ;; ⇒ true (listofp 'string '(no)) ;; ⇒ false ;; Register it (deftype listof (τ) `(satisfies (lambda (thing) (listofp (quote ,τ) thing)))) ;; Use it (typep '(1 2 ) 'list) ;; ⇒ true (typep '(1 two) 'list) ;; ⇒ true (typep '(1 2) '(listof integer)) ;; ⇒ true (typep '(1 "2") '(listof string)) ;; ⇒ false (typep '(1 "2") '(listof (or integer string))) ;; ⇒ true
Notice that by the last example we can control the degree of heterogeneity in our lists! So cool!
Here's some more exercises. The first should be nearly trivial, the second a bit more work, and the last two have made me #sad.
(rose τ)
whose elements are either τ values or rose trees of type τ.record
so that (record τ₁ … τₙ)
denotes a record type whose iᵗʰ
component has type τᵢ
.
Define a type constructor ∃
such that, for example, (∃ τ (pair integer τ)
denotes the type of pairs where the first components are integers and the second
components all have the same type τ
, but we do not know which one.
My idea was to let τ
denote the type of the first occurrence of a value
at that location, then all subsequent checks now refer to this value of τ
.
Sadly, I could not define this type :'(
Upon further reading, this may be doable using a variable watcher.
Produce a record for monoids and keeptrack of the monoid instances produced.
Define a the predicate (monoid τ)
to check if any of the monoid instances
has τ
as its carrier type. In this way we could simulate Haskell typeclasses.
Let me know if you do cool things!
Consider the Haskell expression type, example, and integer evaluator.
data Expr a = Var a  Expr a :+: Expr a  Neg (Expr a) deriving Show ex :: Expr Int ex = Var 5 :+: (Var 6 :+: Neg (Var 7)) int :: Expr Int > Int int (Var n) = n int (l :+: r) = int l + int r int (Neg e) =  (int e) { int ex ⇒ 4 }
If we view a constructor declaration C a₁ … aₙ
with superfluous parenthesis
as (C a₁ … aₙ)
, then a translation to Lisp immediately suggests itself:
Haskell constructors ≅ Lisp lists whose car
are constructor names
A nearly direct translation follows.
(defun exprp (τ thing) (pcase thing (`(var ,n) (typep n τ)) (`(add ,l ,r) (and (exprp τ l) (exprp τ r))) (`(neg ,e) (exprp τ e)))) (setq ex '(add (var 5) (add (var 6) (neg (var 7))))) (exprp 'integer ex) ;; ⇒ true ; This declarion “declaretype” is defined near the end of this article. (declaretype int : (exprof integer) integer) (defun int (thing) (pcase thing (`(var ,n) n) (`(add ,l ,r) (+ (int l) (int r))) (`(neg ,e) ( (int e))))) (int ex) ;; ⇒ 4
There are ofcourse much better ways to do this in Lisp; e.g.,
use identity, +, 
inplace of the var, add, neg
tags
to produce “syntax that carries its semantics”
or express the interpreter int
as a one liner
by replacing the formal tags with their interpretations then
invoking Lisps eval
. I doubt either of these are new ideas,
but the merit of the former seems neat —at a first glance, at least.
Support for ADTs in Common Lisp along with seemingly less clunky pattern matching can be found here —which I have only briefly looked at.
The Haskell presentation has typechecking baked into it, yet our
Lisp interpreter int
does not! This seems terribly worrying, but
that declaretype
declaration actually handles type checking for us!
;; Register the type (deftype exprof (τ) `(satisfies (lambda (thing) (exprp (quote ,τ) thing)))) ;; Try it out (typep '(1 2) '(exprof integer)) ;; ⇒ nil (typep ex '(exprof integer)) ;; ⇒ true ;; This invocation, for example, now yields a helpful error message. (int '(var 6 4)) ;; ;; ⇒ int: Type mismatch! Expected (exprof integer) for argument 0 ≠ Given cons (var 6 4). ;; ;; Which is reasonable since the ‘var’ constructor only takes a single argument.
Notice that invalid cases yield a helpful (runtime) error message!
Lisp gets a bad rap for being untyped; let's clarify this issue further!
It is important to realise that nearly every language is typed —albeit the checking may happen at different stages— and so, as Benjamin Pierce says: Terms like “dynamically typed” are arguably misnomers and should probably be replaced by “dynamically checked,” but the usage is standard.
In particular, dynamically typed is not synonymous with untyped, though some people use it that way since nearly every language is typed —possibly with a single anonymous type.
Some people in the Haskell community, which I love, say things like “if it typechecks, ship it” which is true more often than not, but it leads some people to avoid producing unit tests. For example, the following type checks but should be unit tested.
mcbride :: [Int] > Int mcbride xs = if null xs then head xs else 666
Regardless, I love static type checking and static analysis in general. However, the shift to a dynamically checked setting has resulted in greater interest in unit testing. For example, Haskell's solution to effectful computation is delimited by types, as any Haskeller will proudly say (myself included); but ask how are such computations unit tested and the room is silent (myself included).
Interestingly some unit tests check the typing of inputs and output, which is a mechanical process with no unknowns and so it should be possible to produce a syntax for it using Lisp macros. This is one of the goals of this article and we'll return to it later.
Even though I like Lisp, I'm not sure why dynamic typing is the way to go —c.f. Dynamic Languages are Static Languages which mentions the unjust tyranny of unityped systems. Below are two reasons why people may dislike static types.
First: The defacto typing rule do binary choice is usually:
T : 𝔹 E : α B : α  if T then E else B : α
That means valid programs such as if True then 1 else "two"
are rejected;
even though the resulting type will always be an integer there is no way to know
that statically —the choice needs to be rewritten, evaluated at run time.
Indeed, in Haskell, we would write
if True then Left 1 else Right "two"
which has type Either Int String
,
and to use the resulting value means we need to pattern match or use
the eliminator (
) —from Haskell's Control.Arrow
.
Second:
Some statically typed languages have super weak type systems and ruin the rep
for everyone else.
For example, C
is great and we all love it ofcourse, but it's a shame that we can only
express the polymorphic identity function \(id : ∀{α}. α → α \;=\; λ x → x\),
by using the Cpreprocessor —or dismiss the types by casting pointers around.
Maybe this video is helpful, maybe not: The Unreasonable Effectiveness of Dynamic Typing for Practical Programs
( For the algebraist: Dynamic typing is like working in a monoid whose composition operation is partial and may abruptly crash; whereas static typing is working in a category whose composition is proudly typed. )
Overall I haven't presented a good defence for being dynamically checked, but you should ignore my blunder and consider trying Lisp yourself to see how awesome it is.
I haven't a clue. Here are two conjectures.
First: Code that manipulates code is difficult to type.
Is the type of '(+ x 2)
a numeric code expression?
Or just an arbitrary code expression? Am I allowed to “look inside”
to inspect its structure or is it a black box? What about the nature of
its constituents? If I'm allowed to look at them, can I ask if they're even defined?
What if c
is a code element that introduces an identifier, say it
.
What is type of c
? What if it doesn't introduce and thus avoids accidentally
capturing identifiers? Are we allowed only one form or both? Which do we select
and why?
I may be completely wrong, but below I mention a bunch of papers that suggest it's kind hard to type this stuff.
Second: The type theory just wasn't in place at the time Lisp was created.
Here's a probably wrong account of how it went down.
Typeᵢ : Typeᵢ₊₁
.With his awesome hairdo, John McCarthy gifts the world an elegant piece of art: Lisp (•̀ᴗ•́)و
Lisp introduces a bunch of zany ideas to CS:
read, eval, load, compile, print
are all functions!Π, Σ
types.X's camel .<becomes .~(self .<aware>.)>. —the other camel [does the same].
2019: Coq is selfaware; Agda is playing catchup.
A more informative historical account of Lisp & its universal reverence can be read at: How Lisp Became God's Own Programming Language.
Besides Common Lisp, “Typed Lisps” include an optional type system for Clojure —see also Why we're no longer using Core.typed— Typed Racket, and, more recently, Lux ≈ Haskell + ML + Lisp and Shen ≈ Haskell + Prolog + Lisp.
For example, Common Lisp admits strong static typing, in SBCL, as follows.
; Type declaration then definition. (declaim (ftype (function (fixnum)) numid)) (defun numid (n) n) (defun stringid (s) (declare (string s)) (numid s)) ; in: DEFUN STRINGID ; (NUMID S) ; ; caught WARNING: ; Derived type of S is ; (VALUES STRING &OPTIONAL), ; conflicting with its asserted type ; FIXNUM.
Such annotations mostly serve as compiler optimisation annotations and, unfortunately, Emacs Lisp silently ignores Common Lisp declarations such as ftype —which provides function type declarations. However, Emacs Lisp does provide a method of dispatch filtered by classes rather than by simple types. Interestingly, Lisp methods are more like Haskell typeclass constituents or C# extensible methods rather than like Java object methods —in that, Lisp methods specialise on classes whereas Java's approach is classes have methods.
Here's an example.
(defmethod doit ((n integer)) "I'm an integer!") (defmethod doit ((s string)) "I'm a string!") (defmethod doit ((type (eql :hero)) thing) "I'm a superhero!") (doit 2) ;; ⇒ I'm an integer! (doit "2") ;; ⇒ I'm a string! (doit 'x) ;; ⇒ Error: No applicable method (doit :hero 'bobert) ;; ⇒ I'm a superhero! ;; Ch o cldefmethod ⇒ see extensible list of specialisers Elisp supports.
We can ofcourse make our own classes:
(defclass person () ((name))) (defmethod speak ((x person)) (format "My name is %s." (slotvalue x 'name))) (setq p (makeinstance 'person)) (setf (slotvalue p 'name) "bobert") (speak p) ;; ⇒ My name is bobert. ;; Inherits from ‘person’ and has accessor & constructor methods for a new slot (defclass teacher (person) ((topic :accessor teachertopic :initarg :studying))) (defmethod speak ((x teacher)) (format "My name is %s,and I study %s." (slotvalue x 'name) (teachertopic x))) (setq ins (makeinstance 'teacher :studying "mathematics")) (setf (slotvalue ins 'name) "Robert") (speak ins) ;; ⇒ My name is Robert, and I study mathematics.
Later in this article, we'll make something like the declaim
above
but have it be effectful at runtime. Typing as Macros!
( If you happen to be interested in looking under the hood to see what compiler generated code looks like use
disassemble
. For example, declare(defun go (x) (+ 1 x) 'bye)
then invoke(disassemble 'go)
to see something likevarref x⨾ add1⨾ discard ⨾ constant bye⨾ return
. )
⇨ Each primitive type has a corresponding Lisp function that checks whether an object is a
member of that type. Usually, these are the type name appended with p
, for multiword
names, and p
for single word names. E.g., string
type has the predicate stringp
.
Objects holding information about types.
This is a record
; the typeof
function returns the first slot of records.
This section is based GNU Emacs Lisp Reference Manual, §2.3 “Programming Types”.
Numbers, including fractional and nonfractional types.
integer 
float 
number 
natnum 
zero 
plus 
minus 
odd 
even 
The relationships between these types are as follows:
(numberp x) ≈ (or (integerp x) (floatp x)) 
(natnump x) ≈ (and (integerp x) (≤ 0 x)) 
(zerop x) ≈ (equal 0 x) 
(plusp x) ≈ (< 0 x) 
(minusp x) ≈ (> 0 x) 
(evenp x) ≈ (zerop (mod x 2)) 
(oddp x) ≈ (not (oddp x)) 
Integer: Numbers without fractional parts.
There is no overflow checking.
(expt 2 60) ;; ⇒ 1,152,921,504,606,846,976 (expt 2 61) ;; ⇒ 2,305,843,009,213,693,952 (expt 2 62) ;; ⇒ 0
Numbers are written with an optional sign ‘+’ or ‘’ at the beginning and an optional period at the end.
1 ≈ 1. 
1 ≈ +1 ≈ 1. 
They may also take inclusive (and exclusive) ranges:
The type list (integer LOW HIGH)
represents all integers between
LOW
and HIGH
, inclusive. Either bound may be a list of a single
integer to specify an exclusive limit, or a *
to specify no
limit. The type (integer * *)
is thus equivalent to integer
.
Likewise, lists beginning with float
, real
, or number
represent numbers of that type falling in a particular range.
( The Emacs Common Lisp Documentation )
(typep 4 '(integer 1 5)) ;; ⇒ true since 1 ≤ 4 ≤ 5. (typep 4 '(integer 1 3)) ;; ⇒ nil since 1 ≤ 4 ≰ 3. (typep 12 'integer) ;; ⇒ t (typep 12 'number) ;; ⇒ t (typep 23 'odd) ;; ⇒ t (typep 12 '(integer * 14)) ;; ⇒ t, since 12 ≤ 14, but no lower bound. (typep 12 '(integer 0 *)) ;; ⇒ t; the ‘*’ denotes a wildcard; anything. (typep 1 '(not (integer 0 *))) ;; ⇒ t (typep 1 '(not (integer 0 *))) ;; ⇒ nil (typep 1 '(integer 1 2)) ;; ⇒ t, including lower bound (typep 1 '(integer (1) 2)) ;; ⇒ nil, excluding lower bound (typep 1.23 '(float 1.20 1.24)) ;; ⇒ t ;; Here's a slighly organised demonstration: (typep 1.23 'number) ;; ⇒ t (typep 123 'number) ;; ⇒ t (typep 1.23 'real) ;; ⇒ t (typep 123 'real) ;; ⇒ t (typep 1.23 'integer) ;; ⇒ nil (typep 123 'integer) ;; ⇒ t (typep 1.23 'fixnum) ;; ⇒ nil (typep 123 'fixnum) ;; ⇒ t (typep 1.23 'float) ;; ⇒ t (typep 123 'float) ;; ⇒ nil (typep 123.0 'float) ;; ⇒ t
15.0e+2 ≈ 1500.0
and 1.0e+INF
for negative infinity.real
is a synonym for number
, fixnum
is a
synonym for integer
, and wholenum
is a synonym for natnum
.
The smallest and largest values representable in a Lisp integer are in the
constants mostnegativefixnum
and mostpostivefixnum
;; Relationship with infinities (< 1e+INF mostnegativefixnum mostpositivefixnum 1e+INF) ;; ⇒ t
Representation of letters, numbers, and control characters.
A character is just a small integers, up to 22 bits;
e.g., character A
is represented as the integer 65.
One writes the character ‘A’ as ?A
, which is identical to 65.
Punctuations ()[]\;"'`#
must be escaped; e.g.,
?\( ≈ 40 
?\\ ≈ 92 
Whereas ?. ≈ 46
.
(characterp ?f) ;; ⇒ t (characterp t) ;; ⇒ nil
Emacs specfic characters controlg Cg
, backspace Ch
, tab Ci
, newline Cj
, space,
return, del, and escape are expressed by ?\a, ?\b, ?\t, ?\n, ?\s, ?\r, ?\d, ?\e.
Generally, control characters can be expressed as ?\^𝓍 ≈ ?\C𝓍
,
and meta characters by ?\M𝓍
; e.g., CMb
is expressed
?\M\Cb ≈ ?\C\Mb
.
Finally, ?\S𝓍
denotes shifted𝓍 characters.
There are also ?\H𝓍, ?\A𝓍, ?\s𝓍
to denote Hyper Alt or Supermodified keys;
note that lower case ‘s’ is for super whereas capital is for shift,
and lower case with no dash is a space character.
A multiuse object that refers to functions and variables, and more.
A symbol is an object with a name; different objects have different names.
(typep 'yes 'symbol) ;; ⇒ true (symbolp 'yes) ;; ⇒ true (typep 12 'symbol) ;; ⇒ false (symbolp 12) ;; ⇒ false
symbol ≈ Is it a symbol? 
bound ≈ Does it refer to anything? 
(typep 'xyz 'bound) ;; ⇒ nil (setq xyz 123) (typep 'xyz 'bound) ;; ⇒ t
See this short docs page for more info on when a variable is void.
Names have a tremendously flexible syntax.
(setq +*/_~!@$%^&:<>{}? 23) (setq \+1 23) ;; Note +1 ≈ 1, a number. (setq \12 23) (setq this\ is\ woah 23) ;; Escaping each space! (+ this\ is\ woah 1) ;; ⇒ 24
If the symbbol name starts with a colon ‘:’, it's called a keyword symbol and automatically acts as a constant.
(typep :hello 'keyword) ;; ⇒ t
Symbols generally act as names for variables and functions, however there are
some names that have fixed values and any attempt to reset their values signals an error.
Most notably, these include t
for true or the topmost type,
nil
for false or the bottommost type, and keywords.
These three evaluate to themselves.
t ;; ⇒ t nil ;; ⇒ nil :hello ;; ⇒ :hello (setq t 12) ;; ⇒ Error: Attempt to set a constant symbol (setq nil 12) ;; ⇒ Error: Attempt to set a constant symbol (setq :x 12) ;; ⇒ Error: Attempt to set a constant symbol ;; :x ≠ 'x (set 'x 12) ;; ⇒ 12 x ;; ⇒ 12 ;; They're selfevaluating (equal t 't) ;; ⇒ t (equal nil 'nil) ;; ⇒ t (equal :x ':x) ;; ⇒ t (equal :x 'x) ;; ⇒ nil
In particular, :x ≠ 'x
!
The interface for ordered collections; e.g.,
the (elt sequence index)
function can be applied to any sequence
to extract an element at the given index.
sequence 
seq 
The latter is an extensible variant of the former —for when we declare our own sequential data types.
(typep '(1 2 3) 'sequence) ;; ⇒ t
There are two immediate subtypes: array
and cons
, the latter has list
as a subtype.
(typep [1 2 3] 'array) ;; ⇒ t (typep '(1 2 3) 'cons) ;; ⇒ t (typep '(1 "2" 'three) 'list) ;; ⇒ t
Arrays include strings and vectors.
t
or nil
.(typep "hi" 'string) ;; ⇒ true (typep 'hi 'string) ;; ⇒ false
Cons cells and lists, which are chains of cons cells.
These are objects consisting of two Lisp objects, called car
and cdr
.
That is they are pairs of Lisp objects.
'(x₀ x₁ x₂) ≈ '(x₀ . (x₁ . (x₂ . nil))) ≠ '(x₀ x₁ . x₂) ≈ '(x₀ . (x₁ . x₂))
Notice that when there is no ‘.’, then a list
is just a nested cons chain ending in ‘nil’.
Note that '(x₀ . x₁ . x₂)
is meaningless.
Cons cells are central to Lisp and so objects which are not a cons cell are called atoms.
;; An atom is not a cons. (typep 123 'atom) ;; ⇒ t (typep 'ni 'atom) ;; ⇒ t
Computationally:
(atom x) 

≈  (typep x 'atom) 
≈  (not (consp x)) 
≈  (not (typep x 'cons)) 
≈  (typep x '(not cons)) 
Interestingly, one writes atom
, not atomp
.
Piece of executable code.
A noncompiled function in Lisp is a lambda expression: A list whose
first element is the symbol lambda
.
(consp (lambda (x) x)) ;; ⇒ true (functionp (lambda (x) x)) ;; ⇒ true (functionp (lambda is the first)) ;; ⇒ true (typep (lambda stuff) 'function) ;; ⇒ true
It may help to know that a defun
just produces an alias for a function:
(defun name (args) "docs" body) ≈ (defalias (quote name) (function (lambda (args) docs body)))
Here's some more examples.
(typep #'+ 'function) ;; ⇒ true (typep 'nice 'function) ;; ⇒ false (defun it (x) (format "%s" (+1 x))) (typep #'it 'function) ;; ⇒ true (functionp #'it) ;; ⇒ true
A method of expanding an expression into another expression.
Like functions, any list that begins with macro
, and whose cdr
is a function, is considered a macro as long as Emacs Lisp is concerned.
(macrop '(macro (lambda (x) x))) ;; ⇒ true
Since defmacro
produces an alias, as follows,
(defmacro name (args) "docs" body) ≈ (defalias (quote name) (cons (quote macro) (function (lambda (args) docs body))))
You may be concerned that (macrop x) ≟ (equal 'macro (car x))
, and so if a user
gives you a macro you might think its a cons cell of data.
Fortunately this is not the case:
(defmacro noop () ) (macrop #'noop) ;; ⇒ true (consp #'noop) ;; ⇒ false; whence it's also not a list. (functionp #'noop) ;; ⇒ false (typep #'noop ' (satisfies (lambda (x) (and (listp x) (equal 'macro (car x)))))) ;; ⇒ false
Why not? Well, you could think of a macro as a ‘record’ whose label is macro
and
its only element is the associated function.
Compound objects with programmerdefined types.
They are the underlying representation of defstruct
and defclass
instances.
For example:
(defstruct person
name age)
The typeof
operator yields the car
of instances of such declartions.
(record τ e₀ … eₙ) ≈ #s(τ e₀ … eₙ) 
(setq bobert (makeperson :name "bobby" :age 'toomuch)) (typeof bobert) ;; ⇒ person
Componenets may be indexed with aref
.
(aref bobert 1) ;; ⇒ bobby (personname bobert) ;; ⇒ bobby
A record is considered a constant for evaulation: Evaluating it yields itself.
(typeof #s(person "mark" twelve)) ;; ⇒ person (recordp #s(nice)) ;; ⇒ t
Checking the type of inputs is tedious and so I guessed it could be done using
macros and advice. Looking at Typed Racket for inspiration, the following
fictitious syntax would add advice to f
that checks the optional arguments xᵢ
have type σᵢ
and the mandatory positional arguments have type τᵢ
according
to position, and the result of the computation is of type τ
.
To the best of my knowledge, no one had done this for Emacs Lisp —I don't know why.
(declaretype 'f ((:x₁ σ₁) … (:xₘ σₘ)) (τ₁ … τₙ τ))
To modify a variable, or function, we may simply redefine it; but a much more elegant and powerful approach is to “advise” the current entity with some new behaviour. In our case of interest, we will advise functions to check their arguments before executing their bodies.
Below is my attempt: declaretype
. Before you get scared or think it's horrendous, be charitable and
note that about a third of the following is documentation and a third is local declarations.
(cldefmacro declaretype (f keytypes &rest types) "Attach the given list of types to the function ‘f’ by advising the function to check its arguments’ types are equal to the list of given types. We name the advice ‘⟪f⟫typingadvice’ so that further invocations to this macro overwrite the same advice function rather than introducing additional, unintended, constraints. Using type specifiers we accommodate for unions of types and subtypes, etc ♥‿♥. ‘keytypes’ should be of the shape (:x₀ t₀ ⋯ :xₙ tₙ); when there are no optional types, use symbol “:”. E.g., (declaretype myfunc (:z string :w integer) integer symbol string) " ;; Basic coherency checks. When there aren't optional types, keytypes is the “:” symbol. (should (and (listp types) (or (listp keytypes) (symbolp keytypes)))) (letf* ((pairify (lambda (xs) (loop for i in xs by #'cddr ;; Turn a list of flattenned pairs for j in (cdr xs) by #'cddr ;; into a list of explicit pairs. collect (cons i j)))) ;; MA: No Lisp method for this!? (resulttype (car (takelast 1 types))) (types (droplast 1 types)) (numoftypes (length types)) (keytypesog (unless (symbolp keytypes) keytypes)) (keytypes (funcall pairify keytypesog)) (advicename (intern (format "%stypingadvice" f))) (notifyuser (format "%s now typed %s → %s → %s." `,f keytypesog types resulttype))) `(progn (defun ,advicename (origfun &rest args) ;; Split into positional and key args; optionals not yet considered. (letf* ((allargs (splitat (or (findindex (not (sblank? (ssharedstart ":" (format "%s" it)))) args) ,numoftypes) args)) ;; The “or” is for when there are no keywords provided. (posargs (car allargs)) (keyargs (funcall ,pairify (cadr allargs))) (funresult nil) ((symbolfunction 'shucks) (lambda (eτ e g) (unless (typep g eτ) (error "%s: Type mismatch! Expected %s %s ≠ Given %s %s." (function ,f) eτ e (typeof g) (prin1tostring g)))))) ;; Check the types of positional arguments. (unless (equal ,numoftypes (length posargs)) (error "%s: Insufficient number of arguments; given %s, %s, but %s are needed." (function ,f) (length posargs) posargs ,numoftypes)) (loop for (ar ty pos) in (zip posargs (quote ,types) (numbersequence 0 ,numoftypes)) do (shucks ty (format "for argument %s" pos) ar)) ;; Check the types of *present* keys. (loop for (k . v) in keyargs do (shucks (cdr (assoc k (quote ,keytypes))) k v)) ;; Actually execute the orginal function on the provided arguments. (setq funresult (apply origfun args)) (shucks (quote ,resulttype) "for the result type (!)" funresult) ;; Returnvalue should be given to caller. funresult)) ;; Register the typing advice and notify user of what was added. (adviceadd (function ,f) :around (function ,advicename)) ,notifyuser )))
declaretype
There are some notable shortcomings: Lack of support for type variables and, for now, no support for optional arguments. Nonetheless, I like it —of course. ( Using variable watchers we could likely add support for type variables as well as functiontypes. )
We accidentally forgot to consider an argument.
(declaretype f₁ (:z string :w list) integer symbol string) ;; ⇒ f₁ now typed (:z string :w integer) → (integer symbol) → string. (cldefun f₁ (x y &key z w) (format "%s" x)) ;; ⇒ f₁ now defined (f₁ 'x) ;; ⇒ f₁: Insufficient number of arguments; given 2, (x), but 3 are needed.
The type declaration said we needed 3 arguments, but we did not consider one of them.
We accidentally returned the wrong value.
(declaretype f₂ (:z string :w list) integer symbol string) (cldefun f₂ (x y &key z w) x) (f₂ 144 'two) ;; ⇒ f₂: Type mismatch! Expected string for the result type (!) ≠ Given integer 144.
We accidentally forgot to supply an argument.
(declaretype f₃ (:z string :w list) integer symbol string) (cldefun f₃ (x y &key z w) (format "%s" x)) (f₃ 144) ;; ⇒ f₃: Insufficient number of arguments; given 1, (144), but 2 are needed.
A positional argument is supplied of the wrong type.
(f₃ 'one "two") ;; ⇒ f₃: Type mismatch! Expected integer for argument 0 ≠ Given symbol one. (f₃ 144 "two") ;; ⇒ f₃: Type mismatch! Expected symbol for argument 1 ≠ Given string "two".
Notice: When multiple positional arguments have typeerrors, the errors are reported one at a time.
A keyword argument is supplied of the wrong type.
(f₃ 1 'two :z 'no₀ :w 'no₁) ;; ⇒ f₃: Type mismatch! Expected string :z ≠ Given symbol no₀. (f₃ 1 'two :z "ok" :w 'no₁) ;; ⇒ f₃: Type mismatch! Expected string :w ≠ Given symbol no₁. (f₃ 1 'two :z "ok" :w 23) ;; ⇒ f₃: Type mismatch! Expected string :w ≠ Given integer 23. (f₃ 1 'two :z "ok" :w '(a b 1 2)) ;; ⇒ okay; no typeerror.
We have no optional arguments.
(declaretype f₄ : integer symbol string) (cldefun f₄ (x y &key z w) (format "%s" x)) (f₄ 144 'two :z "bye") ;; ⇒ f₄: Type mismatch! Expected nil :z ≠ Given string "bye". ;; ( We shouldn't have any keyword :z according to the type declaration! ) (f₄ 144 'two) ;; ⇒ "144"
We can incorporate type specfiers such as unions!
(declaretype f₅ : (or integer string) string) (cldefun f₅ (x) (format "%s" x)) (f₅ 144) ;; ⇒ "144" (f₅ "neato") ;; ⇒ "neato" (f₅ 'shakawhenthewallsfell) ;; ⇒ f₅: Type mismatch! Expected (or integer string) for argument 0 ;; ≠ Given symbol shakawhenthewallsfell.
No positional arguments but a complex optional argument!
(declaretype f₆ (:z (satisfies (lambda (it) (and (integerp it) (= 0 (mod it 5)))))) character) (cldefun f₆ (&key z) ?A) (f₆ 'hi) ;; ⇒ Keyword argument 144 not one of (:z) (f₆) ;; ⇒ 65; i.e., the character ‘A’ (f₆ :z 6) ;; ⇒ f₆: Type mismatch! ;; Expected (satisfies (lambda (it) (and (integerp it) (= 0 (mod it 5))))) :z ;; ≠ Given integer 6. (f₆ :z 10) ;; ⇒ 65; i.e., the expected output since 10 mod 5 ≈ 0 & so 10 is valid input.
Preconditions! The previous example had a complex type on a keyword, but that was essentially a precondition; we can do the same on positional arguments.
(declaretype f₇ : (satisfies (lambda (it) (= it 5))) integer) (cldefun f₇ (n) n) ;; The identity on 5 function; and undefined otherwise. (f₇ 4) ;; ⇒ f₇: Type mismatch! Expected (satisfies (lambda (it) (= it 5))) for argument 0 ;; ≠ Given integer 4. (f₇ 5) ;; ⇒ 5
Postconditions! Given an integer greater than 5, we present an integer greater than 2; i.e., this is a constructive proof that \(∀ n • n > 5 ⇒ n > 2\).
(declaretype f₈ : (satisfies (lambda (in) (> in 5))) (satisfies (lambda (out) (> out 2)))) (cldefun f₈ (n) n) ;; The identity on 5 function; and undefined otherwise. (f₈ 4) ;; ⇒ f₈: Type mismatch! Expected (satisfies (lambda (in) (> in 5))) for argument 0 ;; ≠ Given integer 4. (f₈ 72) ;; ⇒ 72; since indeed 72 > 5 for the input, and clearly 72 > 2 for the output.
As it currently stands we cannot make any explicit references between the inputs
and the output, but that's an easy fix: Simply add a local function old
to the
declaretype
macro which is intentionally exposed so that it can be used in the
type declarations to refer to the ‘old’, or initial, values provided to the function.
Additionally, one could also add keyword arguments :requires
and :ensures
for a more sophisticated pre and postcondition framework.
Something along these lines is implemented for Common Lisp.
Here's a fun exercise: Recast the Liquid Haskell examples in Lisp using this
declaretype
form.
I have heard more than one LISP advocate state such subjective comments as, "LISP is the most powerful and elegant programming language in the world" and expect such comments to be taken as objective truth. I have never heard a Java, C++, C, Perl, or Python advocate make the same claim about their own language of choice.
I learned a lot of stuff, hope you did too ^_^
Neato web articles:
After defining declaretype
I thought the slogan “types by macros” sounded nifty;
Googling it led me to this paper where the Racket is endowed with types.
Lisp is great lol.
Herein I try to make my current doctoral research accessible to the average person: Extending dependentlytyped languages to implement module system features in the core language. It's something I can direct my family to, if they're inclined to know what it is I've been doing lately.
The technical matter can be seen at the associated website ─The Next 700 Module Systems─ which includes a poster, slides, and a demo.
Excluding the abstract, this is my thesis proposal in three minutes (•̀ᴗ•́)و
( Photo by Vitor Santos on Unsplash )
There are so many parts when it comes to packaging:
There's so many parts!
Think about it: On average, a ‘coding language’ actually consists of at least 4 different languages! That's like if your package in the mail had
What a headache!
I'm researching the arithmetic of packages to help software developers manage large projects and reduce complexity & maintenance costs.
My research suggests that packaging can be treated in the same way we treat numbers.
So next time you send a package, think of what it means to me and how many languages are involved!
Do you know what the above program accomplishes? If you do, did you also spot a special edge case?
We aim to present an approach to program proving in C using a minimal Emacs setup so that one may produce literate C programs and be able to prove them correct –or execute them– using a single button press; moreover the output is again in Emacs.
The goal is to learn program proving using the FramaC tool –without necessarily invoking its gui– by loading the source of this file into Emacs then editing, executing, & proving as you read along. One provides for the formal specification of properties of C programs –e.g., using ACSL– which can then be verified for the implementations using tools that interpret such annotation –e.g., FramaC invoked from within our Emacs setup.
Read on, and perhaps you'll figure out how to solve the missing FixMe
pieces 😉
The intent is for rapid editing and checking. Indeed, the Framac gui does not permit editing in the gui, so one must switch between their text editor and the gui. Org mode beginning at the basics is a brief tutorial that covers a lot of Org and, from the getgo, covers “the absolute minimum you need to know about Emacs!”
If anything, this effort can be construed as a gateway into interactive theorem proving such as with Isabelle, Coq, or Agda.
The article aims to be selfcontained –not even assuming familiarity with any C!
The presentation and examples are largely inspired by
 Gilles Dowek's exquisite text Principles of Programming Languages.
 It is tremendously accessible!
 Allan Blanchard's excellent tutorial Introduction to C Program Proof using FramaC and its WP Plugin.
Another excellent and succinct tutorial is Virgile Prevosto's ACSL MiniTutorial. In contrast, the tutorial ACSL By Example aims to provide a variety of algorithms rendered in ACSL.
There are no solutions since it's too easy to give up and look at the solutions that're nearby. Moreover, I intend to use some of the exercises for a class I'm teaching ;)
Despite its age, C is a widely used language and so is available on many platforms. Moreover, many systems rely on code historically written in C that needs to be maintained.
The traditional way to obtain confidence that a program correctly works is to provide inputs we believe to be representative of the actual use of the program and verify the results we get are correct. Incidentally, the unexpected cases are often not considered whereas they are generally the most dangerous ones. Since we cannot test everything, we need to employ great care in selecting good tests.
Since it is hard to answer “Is our software tested enough?”, we consider mathematically proving that there cannot be problems at runtime. That is, a specification of the expected behaviour is provided, which is satisfied by the resulting program –note the order: Specify then obtain code! This twostage process can produce errors in either stage, yet whereas testing ensures “the program avoids the bugs we tested against” this approach is a big step ensuring “the program doesn't contain bugs that don't exist in the specification.”
The goal here is to use a tool to learn the basics of C program proof –FramaC: FRAmework for Modular Analysis of C code. In particular, to demonstrate the ability to write programs without any error by emphasising the simple notions needed to write programs more confidently.
Testing is ‘dynamic analysis’ since it requires the actual execution of programs, whereas our program proof approach is ‘static’ since no execution is performed but instead we reason on a semantic model of the reachable states. The semantics associated with the C language control structures and statements we will use is known as Hoare Logic. One writes a “Hoare Triple” {G} S {R} to express that “Starting in a given state (satisfying) G, the execution of S terminates such that the resulting state (satisfies) R”.
How does one prove such a triple? Programs are constructed from a variety of pieces, so it suffices to look at each of those. That is, {G} S {R} has the code S transform the required predicate R to the given G –since we usually know what is required and it is the goal of the program. In general, we find the weakest precondition that works since then it allows the largest possible number of inputs; if G is at least as strong as the weakest precondition, then our program is correct but may allow less then the largest amount of possible inputs.
For example, {5 ≤ x ≤ 10} x ≔ x + 1 { 3 ≤ x ≤ 11} is a correct program that ends in state 3 ≤ x ≤ 11, however its precondition could be weakened to 2 ≤ x ≤ 10 thereby allowing many more valid inputs.
In FramaC, we do not use curly braces like this for such assertions but instead express our properties as code annotations using the ANSI C Specification Language –ACSL or “axel”. The weakest precondition plugin uses the annotation and the code to automatically ensure the program is correct according to the Hoare Logic fashion mentioned earlier.
The aim of this section is to introduce the Emacs controls that bring C to life within literate code.
Key Press  Elisp Command  Description 
Enter <s then TAB 
─  Produces a “Hello World” C template program. 
F6 
(interpret) 
Execute currently focused code blocks in a new buffer. 
F7 
(showcode) 
Shows currently focused code blocks in a new buffer. 
F8 
(framac) 
Open the FramaC gui on the currently focused code blocks. 
F9 
(framacnogui) 
Invoke FramaC on the currently focused code blocks in a new buffer. 
Which code blocks are currently under focus is controlled by the command
(currentlyworkingwith "nameHere")
, which produces a file nameHere.c
that is used
for the utility functions. If multiple blocks use the same filename, then the file is
appended to.
While reading this tutorial, bring/take code blocks in/out of focus by
toggling between (currentlyworkingwith "nameHere")
and
(notcurrentlyworkingwith "nameHere")
—that is, simply prepend not
.
Remember to undo such a toggle when you're done with a code block.
When no name is provided to [not]currentlyworkingwith
, the name of the buffer is used.
<s
then press the TAB
key.F6
to execute the code in another buffer.F7
to inspect the code in another buffer.F6
.
Now toggle that code block so that you are not
currently working with it.
F6
refers
to the old file on disk since there is no currently focused block.
Consider the fully annotated swap
algorithm, i.e., remove the not
prefix,
// #include<stdio.h> // for IO /*@ requires \valid(a) && \valid(b); assigns *a, *b; ensures *a == \old(*b); ensures *b == \old(*a); */ void swap(int* a, int* b) { int tmp = *a; *a = *b; *b = tmp; } int main() { // printf("Hello World!\n"); int a = 42; int b = 37; //@ assert Before_Swap: a == 442 && b == 37; swap(&a, &b); //@ assert After_Swap: a == 37 && b == 42; return 0; }
We can see that FramaC proves these assertions by obtaining “green bullets” beside them if we execute
framacgui wp rte myfile.c
Or checkboxes beside them if we instead execute
framacgui guitheme colorblind wp rte myfile.c
The best way to know which options are available is to use
framac wphelp
We will however use the special Emacs calls defined at the bottom of this file,
framac
and framacnogui
, to avoid having to switch between a terminal and a
text editor. Thankyou extensible editor Emacs ⌣̈ ♥
F8
to invoke the FramaC gui.F9
to invoke FramaC within Emacs and obtain status about the program proof.If you uncomment the IO matter, FramaC may yield an error. Separate your IO into its own driver file! Invoke FramaC only on programs you want to prove –without any IO.
Go back to the above example, and change the first assertion in main,
~a == 42
, to assert that a
equals 432
. Now invoke Mx framacnogui
, or press F9
,
to obtain the message,
FramaC: 90﹪ of proof complete!
Moreover, another buffer will be open and in red will be highlighted,
[wp] [AltErgo] Goal typed_main_assert_Before_Swap : Unknown (Qed:0.63ms) (57ms)
This indicates that, in method main
, the named assertion Before_Swap
could not be proven.
Now revert all alterations and in the specification of swap
, alter
ensures *a ==== \old(*b);
to become ensures *a == \old(*a);
, thereby expressing
that the value of a
is unaltered by the program. Checking this yields a false
assertion! Neato.
As such, I suggest the following literate process:
framacnogui
(F9) or framac
(F8).
Prove function annotations by WP
to have our assertions checked.Observe
Since C's #include
is essentially a copypaste, we can reexport other libraries
from a makeshift ‘Prelude’.
// Artefacts so that exercises let the system progress as much as possible. // //@ predicate Exercise = \false; //@ predicate FixMe = \false; #define FixMeCode // Tendency to require this header file to specfiy avoidance of overflow. // #include<limits.h>
We will continue to be (currentlyworkingwith "Prelude")
in the future to add more
content. For now, we put the artefacts needed to let the exercises pass.
The use of
\false
is not the most appropriate, since its occurrence in a precondition vacuously makes everything true! This is something that should change.The current setup produces only
.c
files, whence we use the prelude by declaring#include "Prelude.c".
Forgive my use of a.c
file inplace of a header file. The alternative is to force all code block names to end in a.c
.
Recall that a Hoare Triple {G} S {R} expresses that if execution of program S
is begun
in a state satisfying proposition G then it terminates in a state satisfying proposition R.
We usually know R –it is the required behaviour on S
after all– but otherwise we usually
only have a vague idea of what G could possible be.
Dijkstra's weakest precondition operator ‘wp’ tells us how to compute G from R
–in the process we usually discover S
.
Hence, all in all, programming begins with the required goal from which code is then derived.
Postconditions R are expressed using the ensures
clause, and dually preconditions G
are expressed using requires
clauses. These G are properties assumed for the input
and it is the callers responsibility to ensure that they are true
–recall that when a contract is breached, the implementation may behave arbitrarily.
Since ‘wp’ is intended to compute the weakest precondition establishing a given
postcondition R for a statement S
, it necessarily satisfies
{ G } S { R } ≡ G ⇒ wp S R
The left side may be rendered using ACSL,
// @ assert G; S; // @ assert R;
The WP plugin for FramaC essentially works by computing wp S R
then attempts to obtain
a proof for G ⇒ wp S R
.
In particular, by the reflexivity of implication, ‘wp’ guarantees to produce a
precondition so that the following Hoare triple is valid.
{ wp S R } S { R }
Most programming languages have, among others, five constructs:
Assignment, variable declaration, sequence, test, and loop.
These constructs from the imperative core of the language.
Since programs are built using such primitive control structures, it suffices to define
wp “by induction” on the shape of S
.
One reasonable property we impose on wp from the outset is:
If S establishes R which implies R′, then S also establishes R′.
Monotonicity: R ⇒ R′ implies wp S R ⇒ wp S R′ That is, {wp S R} S {R′}
Whence for each definitional clause of wp, we must ensure this desirable property is held.
Imperative programs alter state, as such a statement S
is essentially a function
that transforms the memory state of the computer.
Expressing in English what happens when a statement is executed is possible
for simple examples, but such explanations quickly become complicated and imprecise.
Therefore, one introduces a theoretical framework reifying statements as state transformers.
The two popular notions are the “forwards” ⟦S⟧
moves current state to a new state,
whereas we are working with “backwards” wp S
which takes a desired state and yields
a necessary previous state. The forward notion ‘executes’ a program by starting in
the empty state and stepping through each command to result in some final state.
Whereas the backwards notion takes an end goal and suggests which programming constructs
are needed to obtain it.
Hence, ‘forwards’ is verification whereas ‘backwards’ is correctbyconstruction
programming.
Suppose there is an infinite set Var
of variables and an infinite set Val
of values,
which are integers, booleans, etc. In the ‘forwards’ notion, a state is a partial
function from variables to values –`partial' since it may be undefined at some
variables, since we usually use only finitely many
in our programs anyways. E.g., state {x ↦ 5, y ↦ 6}
associates the value 5 to variable x
but is undefined for variable z
. Dually, in the ‘backwards’ notion, a state is a predicate of the
variables and their values that satisfy the predicate; e.g., the previous example state
corresponds to the predicate x = 5 ∧ y = 6
, where z
can have any value.
Hence the predicate formulation is more relaxed and we shall refer to it instead.
The assignment construct allows the creation of a statement with a variable
x
and an expression E
. The assignment statement is written x = E;
.
x ≔ E
even though it is invalid C code.
To understand the execution of an assignment, suppose that within the
recesses of your computer's memory, there is a compartment labelled x
.
Obtain the value of E
–possibly having to look up values of variables
that E
mentions– then erase the contents of x
and fill the compartment
with the newly obtained value.
The whole of the contents of the computer's memory is called a state. We also say “predicate R is the current state” as shorthand for: The current state is (nondeterministically) any variablevalue assignment such that predicate R is true.
All in all, executing x ≔ E
loads memory location x
with the value of expression E
;
hence state R is satisfied after an assignment precisely when it is satisfied
with variable x
replaced by expression E
. For example, wp (x ≔ x+1) (x = 5) ≡ (x+1 = 5)
.
wp (x ≔ E) R ≡ R[x ≔ E]
Before being able to assign values to a variable x
, it must be declared,
which associates the name x
to a location in the computer's memory.
Variable declaration is a construct that allows the creation of a statement
composed of a variable, an expression, and a statement. This is written
{T x = e; p}
, then variable x
can be used in statement p
, which is called
the scope of variable x
.
( When p
has no assignments, functional programmers would call this statement
a let statement since it lets x
take on the value e
in p
. )
5 / 2.0
to mark the result as floating point.a % b
is a  b * (a / b).
(c) ? t : f
yields t
if boolean c
holds and is f
otherwise.
Imperative languages generally do not allow the declaration of the same variable multiple
times, e.g., the following program crashes with error: redefinition of ‘x’
.
#include<stdio.h> // for IO int main() { int x = 3; printf("x has value: %d", x); int x = 4; printf("x has value: %d", x); return 0; }
However, if we explicitly delimit the scope of a variable by using braces, then we can obtain multiple declarations:
#include<stdio.h> // for IO int main() { int x = 3; printf(" x has value: %d", x); // 3 { int x = 4; printf("\n x has value: %d", x); // 4 } return 0; }
When explicitly delimiting scope, it is the most recent declarations that are used. We say that earlier declarations are hidden, or overshadowed, by the later declarations.
int main() { int x = 3; //@ assert x == 3; { // Begun new scope // Old facts are still true. //@ assert x == 3; // Now overshadowing ‘x’ int x = 4; // This new ‘x’ is equal to 4. //@ assert x == 4; } // Back to the parent scope. // In this scope, ‘x’ still has its orignal value. //@ assert x == 3; return 0; }
Constant variables are variables which may have only one initial value that
can never be changed. A nonconstant variable is called mutable, which is
the default in imperative languages. For example, the following
crashes with error: assignment of readonly variable ‘x’
.
#include<stdio.h> // for IO int main() { const int x = 3; x = 4; printf("x has value: %d", x); return 0; }
A sequence is a construct that allows a single statement to be created out of
two statements p
and q
; it is written {p q}
.
The sequence is executed in state s
by first executing p
in state s
thereby
producing a new state s'
in which statement q
is then executed.
{p₁ {p₂ { ... pₙ } ...}}
can also be written {p₁ p₂ ... pₙ}
.Usually a ‘;’ symbol is used in favour of a space, with braces, to yield,
wp (S₁;S₂) R ≡ wp S₁ (wp S₂ R)
The precondition of the second statement becomes the postcondition of the first statement. Hence, we “push” along the postcondition into a sequence: In the upcoming swapping example, we read the proof steps from bottom to top!
Rendered pointfree, i.e., ignoring R, this rule becomes: wp (S₁;S₂) = wp S₁ ∘ wp S₂.
Recall that we need to ensure monotonicity is satisfied, and indeed: If R ⇒ R′, then
wp (S₁;S₂) R ≡ wp S₁ (wp S₂ R)  Definition of wp on sequence ⇒ wp S₁ (wp S₂ R′)  Monotoncity for Sᵢ, twice; with assumption R ⇒ R′ ≡ wp (S₁;S₂) R  Definition of wp on sequence
Neato!
Moreover, notice we have the useful ‘transitivity’ property for Hoare triples:
{G} S₁ {R′} ∧ {R′} S₂ {R} ≡ (G ⇒ wp S₁ R′) ∧ (R′ ⇒ wp S₂ R)  Characterisation of wp ⇒ (G ⇒ wp S₁ R′) ∧ (wp S₁ R′ ⇒ wp S₁ (wp S₂ R))  Monotonicity of wp ⇒ (G ⇒ wp S₁ (wp S₂ R))  Transitivity of implication ≡ G ⇒ wp (S₁;S₂) R  Definition of wp on sequence ≡ {G} S₁;S₂ {R}  Characterisation of wp
Exercise: Show that wp (x ≔ E; S) R ≡ (wp S R)[x ≔ E]
.
The “empty sequence” is denoted {}
or just ;
in the C language.
It is also commonly known as the skip
construct and its importance is akin to that
of zero to addition.
wp skip R ≡ R
The “do nothing” program skip
is rendered as simple ;
or as whitespace in the C language.
This program does not alter the state at all, thus it truthifies R precisely when R
was true to begin with.
Most often this appears in a weakening/strengthening form,
...code here... //@ assert P; //@ assert Q; ...code here...
Where P ⇒ Q is provable.
More concretely,
/*@ requires 3 < a < 9; @ ensures 20 <= \result <= 99; */ int using_skip(int a) { //@ assert our_strong_pre: 3 < a < 9; //@ assert weakened_intermediary: 7 <= a <= 14; //@ assert weakening_futher: 20 <= a <= 99; return a; }
Woah! It looks like the identity function somehow transforms input satisfying
3 < x < 9 to input satisfying 20 ≤ x ≤ 99.
Wait, the former implies the latter and that's just the definition of wp on skip
.
The above example suggests the following calculation,
(G′ ⇒ G) ∧ {G} S {R} ∧ (R ⇒ R′) ≡ (G′ ⇒ G) ∧ (G ⇒ wp S R) ∧ (R ⇒ R′)  Characterisation of wp ⇒ (G′ ⇒ wp S R) ∧ (R ⇒ R′)  Transitivity of implication ⇒ (G′ ⇒ wp S R) ∧ (wp S R ⇒ wp S R′)  Monotonicity of wp ⇒ (G′ ⇒ wp S R′)  Transitivity of implication ≡ {G′} S {R′}  Characterisation of wp
That is, strengthening the precondition or weakening the postcondition leaves a Hoare triple valid. In some industry circles –e.g., C#–, this is referred to as contravariance (antitone) in the input and covariance (monotone) in the output.
For example, if G′, G, R, R′
were classes such that G′
is a subclass of G
and R
is a subclass of R′
, then the program S
takes an input of type G
yielding an
output of type R′
. However, any input of type G′
can be cast into the parentclass
type G
and, likewise, R
objects can be cast into the parenttype R′
.
Thus, program S
can also take the type of G′
to R′
.
Writing <:
for ‘subtype’, or ‘subclass’, we have argued,
Provided G′ <: G and R <: R′ Then G → R <: G′ → R′
It is now easier to see that the second argument of functiontype former ‘→’ stays
on the same side of the <:
symbol, whereas it is flipped for the first argument.
Completely unrelated –or not– a nearly identical property holds for implication: If G′ ⇒ G and R ⇒ R′ then (G ⇒ R) ⇒ (G′ ⇒ R′). How coincidental … or not! \\ ( Foreshadowing: CurryHoward Correspondence! )
Anyhow, this strengtheningweakening law will be useful when computing the wp of a statement directly is difficult –and possibly unhelpful– but we have a stronger precondition and so it suffices to use that. ( Foreshadowing: Loops! )
Before we close, here is an exercise to the reader: An alternate proof of the above law.
(G′ ⇒ G) ∧ {G} S {R} ∧ (R ⇒ R′) ≡ {G′} skip {G} ∧ {G} S {R} ∧ {R} skip {R′}  ??? ⇒ {G′} skip; S {R} ∧ {R} skip {R′}  ??? ⇒ {G′} skip; S; skip {R′}  ??? ≡ {G′} S {R′}  skip is noop & can be removed.
The last hint in the above calculation deserves some attention.
S ≈ T
precisely when wp S = wp T
.
S ; skip ≈ S ≈ skip ; S
.abort
is a program that crashes on
any input.S ; abort ≈ abort ≈ abort ; S
.
To avoid having to write the verbose \at(x, Pre)
to refer to the value of a variable x
before method execution, we may use a ghost variable: A variable whose purpose is only
to make the assertions provable, and otherwise is not an executionrelevant variable.
#include<limits.h> /*@ requires \valid(x) && \valid(y); @ requires INT_MIN < *x + *y < INT_MAX; @ requires \separated(x, y); // Exercise: It's a swap, why is this needed? @ assigns *x, *y; */ void swap(int *x, int *y) { //@ ghost int X = *x; //@ ghost int Y = *y; //@ assert *y == Y && *x == X; //@ assert *y == Y && (*x + *y)  *y == X; *x = *x + *y; //@ assert *y == Y && *x  *y == X; //@ assert *x  (*x  *y) == Y && *x  *y == X; *y = *x  *y; //@ assert *x  *y == Y && *y == X; *x = *x  *y; //@ assert *x == Y && *y == X; // 𝓢𝓽𝓪𝓻𝓽 upwards reading from here; // each assertion is obtained by the assigment, skip, and sequence rules. }
Here is a more complicated exercise that also makes use of external functions…
#include "Prelude.c" #define RAND_MAX 32767 /*@ @ assigns \nothing; @ ensures 0 <= \result <= RAND_MAX; */ int rand(); /*@ requires min <= max; @ requires min + RAND_MAX < INT_MAX; @ requires max  min < INT_MAX; @ assigns \nothing; @ ensures min <= \result <= max; */ int random_between(int min, int max) { int it = rand(); //@ assert weakening: 0 <= it <= RAND_MAX; //@ assert assignment_rule_again: FixMe; it = it % (max  min + 1); // @ assert simplify: FixMe; // @ assert assignment_rule: FixMe; it = it + min; // @ assert min <= it; // Start at the bottom, and push assertion upwards! // The assertion names are also intended to be read upwards; // Each justifies how it was obtained. return it; // That is, // return (rand() % (max  min + 1)) + min; }
A test is a statement formed from a Boolean expression and two statements; it is
written \\ if (b) p else q
–sometimes a ‘then’ keyword is used for readability, but
such is not valid C code.
This is executed in a state by evaluating the Boolean
then deciding which branch to execute in the same state.
wp (if B then S₁ else S₂) R ≣ if B then wp S₁ R else wp S₂ R ≡ (B ⇒ wp S₁ R) ∧ (¬ B ⇒ wp S₂ R)
A conditional ensures R precisely when its branches each ensure R.
Observe the following calculation,
{ G } if B then S₁ else S₂ { R } ≡ G ⇒ wp (if B then S₁ else S₂) R  Characterisation of wp ≡ G ⇒ (B ⇒ wp S₁ R) ∧ (¬ B ⇒ wp S₂ R)  Definition of wp on conditional ≡ (G ⇒ B ⇒ wp S₁ R) ∧ (G ⇒ ¬ B ⇒ wp S₂ R)  Characterisation of meets ≡ (G ∧ B ⇒ wp S₁ R) ∧ (G ∧ ¬ B ⇒ wp S₂ R)  Shunting ≡ {G ∧ B} S₁ {R} ∧ {G ∧ ¬ B} S₂ {R}  Characterisation of wp
That is, Hoare triples on a conditional ‘distribute’ into the branches with each branch precondition obtaining the branch guard.
A loop is a construct formed from a Boolean expression and a statement; it is
written while (b) p
.
A loop is one of the ways in which we can express an infinite object –which may
fail to terminate– using a finite expression. Indeed, its executional behaviour
can be understood by realising it as a shorthand for the expression
if (b) {p if (b) {p if (b) ... else skip} else skip} else skip
Where skip
is the fictional statement that performs no action when executed.
To understand the semantics of the loop:
giveup, terminate
be aliases for abort
and skip
.Recalling that a loop is a shorthand for an infinite nesting of conditionals, we try to approach it as a limit of finite approximations.
while (b) q ≈ limₙ pₙ Where: p₀ = if (b) giveup else terminate p₁ = if (b) {q ; if b giveup else terminate} else terminate ⋯ pₙ₊₁ = if (b) {q; pₙ} else terminate;
pₙ
tries to execute the statement while (b) q
by completing
a maximum of n
trips through the loop. If, after n
loops, it has not
terminated on its own, it gives up.If the loop terminates in m trips, it also terminates in n ≥ m trips.
Where ‘defined’ means it terminates without aborting.
while(b) q ≈ limₙ pₙ
.The definition of ‘wp’ for loops is complicated and generally unhelpful, however from it we can prove the so called “Invariance Theorem”: If a property is maintained by the body of the loop and there is an integral value expressed using the body's variables that starts out nonnegative and is decreased by each loop pass, then the loop will terminate and the property it maintained will be true.
{Inv ∧ B ∧ bf = c} S {Inv ∧ bf < c} ⇒ {Inv ∧ bf ≥ 0} while(B) S {¬B ∧ Inv}
A property that is maintained to be true throughout the loop is referred to as an
invariant. In contrast, a value that changes through every pass
–such as the number of passes remaining, the bf
– is known as a variant.
In FramaC rendition,
/*@ loop invariant ⋯ // property that is maintained by the loop body @ loop assigns ⋯ // variables that are altered by the loop body @ loop variant ⋯ // a bound on the total number of loops */ while(B) S;
Incidentally the primary function of the assigns
clause is that variables its does
not mention essentially have the invariant property of being equal to their value
before the loop. That is, the assigns
clause reduces clutter regarding constants
from the invariant!
#include<limits.h> /*@ requires N >= 0; @ requires N * (N + 1) /2 < INT_MAX; @ assigns \nothing; @ ensures \result == N * (N + 1) / 2; */ int euclid(int N) { int sum = 0; int n = 0; //@ assert invariant_intially_estabished: 2 * sum == 0 * (0 + 1); /*@ loop invariant main_item: sum == n * (n + 1) / 2; @ loop invariant always_in_range: 0 <= n <= N; @ loop invariant range_for_sum: 0 <= sum; // Exercise: Why can we comment out the following two lines? @ loop invariant no_overflow1: n < INT_MAX; @ loop invariant no_overflow2: sum <= INT_MAX; @ loop assigns n, sum; @ loop variant N  n; */ while(n != N) { //@ assert sum + n < INT_MAX; n = n + 1; //@ assert sum + n <= INT_MAX; sum = sum + n; //@ assert sum <= INT_MAX; } //@ assert invariant_and_not_guard: n == N && sum == n * (n + 1) / 2; //@ assert post_condition: sum == N * (N + 1) / 2; // rewrite_post: 2 * sum == N * (N + 1); // Not true, due to ‘rounding’! return sum; }
Exercise: Why is sum ≤ INT_MAX
true at the end of the loop body? Fill in the proof:
sum ≤ INT_MAX ≡ n * (n + 1) / 2 ≤ INT_MAX  ??? ⇐ n * (n + 1) / 2 ≤ N * (N + 1) / 2 ≤ INT_MAX  Transtivitity of ≤ ≡ N * (N + 1) / 2 ≤ INT_MAX  ??? ≡ true  ???
Here's a simple exercise,
#include<limits.h> /*@ requires a + 10 < INT_MAX; @ ensures \result == \old(a) + 10; */ int look_ma_no_new_locals(int a) { //@ ghost const int A = a; int i; // So we can refer to this ‘i’ *after* the loop. /* // @ loop assigns ???; // Fix me. @ loop invariant a == 666; // Fix me. @ loop invariant 0 <= i <= 10; @ loop variant 666; // Fix me. @*/ for(i = 0; i != 10; i++) a++; //@ assert after_loop_guarantees: i == 10 && a == A + i; //@ assert weakening_previous_gives: a == A + 10; return a; }
Warning Without the loop assigns
, you are more likely to have trouble proving a loop
is correct!
Now a bit harder…
#include<limits.h> /*@ assigns \nothing; @ ensures 0 <= \result <= 1; */ int random_bool(); // { return random_between(0, 1); } /*@ requires \valid(it); @ requires *it + max <= INT_MAX; // @ requires FixMe  a property on `max`; // @ assigns FixMe; @ ensures \old(it) <= it <= it + max; */ void increment_randomly(int *it, int max) { /*@ loop assigns i, *it; @ loop invariant *it == \at(*it, Pre) + i; // @ loop invariant range_of_i: FixMe; // @ loop variant FixMe; */ for(int i = 0; i != max && random_bool(); i++) (*it)++; }
Our invaraints were getting out of hand, the trouble can be mitigated by defining our own
predicates rather than just using the built in ones. However, such definitions must be
functional in nature: They do not produce sideeffects, such as altering state.
Moreover, they are generally parameterised by a label that refers to the C memory state
in which they would be invoked –within the definition we cannot however reference the
special labels Here, Pre, Post
. Otherwise parameter passing is by value as in C.
The predicate
keyword declares Boolean values functions:
/*@ predicate name_here {Label₀, …, Labelₖ} (type₀ arg₀, …, typeₘ argₘ) = @ // A Boolean valued relationship between all these things. */
// An integer memory location remains unaltered between given program points. // /*@ predicate unchanged{L0, L1} (int *i) = \at(*i, L0) == \at(*i, L1); */ int main() { int i = 13, j = 12; DoSomeWork: j = 32; //@ assert unchanged{DoSomeWork, Here}(&i); return 0; }
More usefully, there is the need to ensure a given integer is indeed a nonnegative length of an array.
/*@ predicate valid_array(int* arr, integer len) = @ 0 <= len && \valid(arr + (0 .. len  1)); */
Notice that we did not specify a memory label.
That's okay, one is provided for us and the entire definition is considered to transpire
at that memory location. In particular, unlike the previous example, we cannot refer to
distinct memory locations –after all we haven't named any!
At the call site, the implicit memory location would be Here
thereby referring to the
current memory state –however we may still explicitly provide a different label at the
call site.
Predicates must be either true or false, but logic
functions are methods that
can be invoked in our specifications –you may have noticed that C methods cannot
be called in a specification, which is reasonably since they may produce sideeffects!
Since assignment, sequence, and loops rely on side effects they now suddenly become
useless and our definitions must rely on recursion.
Such logical functions generally need not worry about runtime issues such as overflow
–which however must be handled at the call site, if need be.
/*@ logic return_type function_name {Label₀, …, Labelₖ} (type₀ arg₀, …, typeₘ argₘ) = @ // A formula using the arguments argᵢ, possibly at labels Labelᵢ */ //@ logic integer factorial(integer n) = (n <= 0) ? 1 : n * factorial(n1);
If we wrote a program that contained many occurrences to factorial
then the definition
would need to be invoked each time. If the occurrences all happened, for example, on the
same input, say 12 –any larger would be an unsigned int
overflow– then it might
make matters faster if we simply had that as a lemma that is proven once and used many
times by the underlying provers. Indeed this can be accomplished by using the lemma
phrase, and this can be done for any property.
//@ lemma name_of_property {Label₀, …, Label₀}: property_here ;
For example,
//@ lemma lt_plus_lt: \forall integer i,j; i < j ==> i + 1 < j + 1;
Sometimes a proof may take too long to be proven, or it cannot be proven with the backend provers, and, moreover, we do not wish to bother with its proof directly. In such cases, we may tell FramaC to trust our judgement and take our word for it –if we're not careful, our ‘word’ may lead us to conclude false = true!
/*@ axiomatic my_axioms { @ axiom antisymmetry: \forall integer i, j; i <= j <= i ==> i == j; @ } */
Unlike lemmas, which require a proof, axioms are simply assumed to be true. It is the responsibility of the user to ensure no inconsistencies arise, as in:
//@ axiomatic UhOh{ axiom false_is_true: \false; } int main() { // Examples of proven properties //@ assert \false; //@ assert \forall integer x; x == 31; //@ assert \false == \true; return 0; }
However, axiomatic
definitions of recursive functions are useful since the underlying
provers do not unroll the recursion when possible
–after all, we are simply declaring the type of a function and some properties about
it, which incidentally, happen to be its defining equations.
/*@ axiomatic Factorial { logic integer factorial(integer n); axiom factorial_base: \forall integer i; i <= 0 ==> factorial(i) == 0; axiom factorial_inductive: \forall integer i; i > 0 ==> factorial(i) == i * factorial(i  1); } */
A small subtlety is that access to memory locations must be specified in the function headers, for example:
/*@ axiomatic is_constant { predicate constant{L}(int * a, integer b, integer e, integer val) reads a[b .. e1]; axiom constant_empty{L}: \forall int * a, integer b, e, val; b >= e ==> constant{L}(a, b, e, val); axiom constant_non_empty{L}: \forall int * a, integer b, e, val; b < e ==> ( constant{L}(a,b,e, val) <==> constant{L}(a,b,e1, val) && a[e1] == val ); } */
We will be proving code blocks satisfy Hoare triples, but code blocks are essentially methods with the given predicate acting as a precondition and the required predicate acting as postcondition. As such, we investigate Hoare triples by using methods.
A contract stipulates under what conditions a method will behave
–if those conditions are not met, then it's behaviour may be arbitrary.
For example, a method may behave in a manner ensuring \\ x > 1/y
under the condition y > 0
,
and it may do anything it wants –such as aborting the system or setting x = 1
–
when that condition is not met.
All in all, a function call establishes property R precisely when evaluating its arguments then executing the function body together establish the property.
wp ℱ(t₁, …, tₙ) R ≡ wp (x₁ ≔ t₁; ⋯ ; xₙ ≔ tₙ; ℬ) R where ℱ(x₁, …, xₙ) = ℬ  Definition of ℱ
Functions permit abstraction over program design since parts may be constructed independently and also promotes avoidance of repetition.
Unlike functional languages where the result of a function is the final
term in its body, imperative languages signal result values by the return
keyword;
which immediately stops execution of the function regardless of the keyword's position.
Functions which return no value but instead perform some effectful action, i.e., are procedures,
are marked with the void
keyword inplace of a return type –surprisingly, this ‘return type’
is not a type at all! One cannot declare a variable of type void
.
Function calls that yield a value are terms whereas those that do not constitute statements.
#include<stdio.h> // for IO int f(){ return 3;} void g(){ printf("g(): Hello There!"); } int main() { // Valid invocations int x = f(); f(); // Bad form! Result is discarded. g(); // T y = g(); // Type error! No possible type T! return 0; }
A program is an sequence of global variables, function declarations, then a special
function called main
. Since sequences are ordered, all names are declared before use!
In C, main
usually exits with return 0
. Global variables tend to pollute
the namespace and are more trouble than they're worth, so we shall ignore them
–however they are already included by default since assignment is a toplevel construct.
Incidentally, function declarations with no arguments may be used to simulate global
constants.
Note that C does not permit function overloading.
Moreover, C uses the same namespace for methods as well as simple variables
–which in is not the case in, say, Java which permits naming a method f
and a
variable f
to obtain the valid invocation f(f)
!
int it(int x){ return x; } int it = 5; int maybe = it(it);
In C, any expression followed by a semicolon is a statement: The value of the expression is simply ignored when it is used as a statement.
Conversely, statements may be regarded as an effectful expression.
For example, assignment x ≔ E
assigns the value of E
to x
and returns the value of E
.
Whence, the notion of
void continued_assgns() { int x = 1, y = 2, z = 4; //@ assert x == 1 && y == 2 && z == 4; x = y += z; // Assignments are rightassociative! //@ assert z == 4 && y == 2+4 && x == 1(2+4); //@ assert z == 4 && y == 6 && x == 5; }
Look at the definition of abs
below and notice:
@
symbol and concluded with a ;
symbol.ensures
clause; which may contain the
\result
keyword to refer to the returned value of the method.ensures
clauses by using conjunction &&
, or have them on
separate lines. We may also use implication ==>
, disjunction 
, negation !
,
value equality ==
, and Boolean equivalence <==>
.
A ==> B
informs that when A
is true then so is B
,
and if A
is false then we don't care and consider the whole thing to be true./*@ ensures always_nonnegative: \result >= 0; @ ensures val > 0 ==> \result == val; @ ensures val < 0 ==> \result == val; */ int abs(int val) { return (val < 0) ? val : val; }
Pressing F9
, you will notice that we are also checking for runtime errors by using the RTE plugin
whose goal is to ensure the program cannot create runtime errors such as integer overflow,
invalid pointer dereferencing, division by 0, etc.
In our case, we have runtime problems that do not crash but instead produce logical errors:
The return value of abs
is not positive! Indeed, in addition to the above block, add focus
to the following block by removing the prefix not
, then run the code with F6
to see the output.
–Remember to undo this alteration when you're done, otherwise the next invocation to framac
will take a while dealing with printf
!
#include<stdio.h> // for IO #include<limits.h> // bounds on integers #include<math.h> int main() { // Our implementation is faulty.. printf(" INT_MIN = %d\n", INT_MIN); printf("abs(INT_MIN) = %d\n\n", abs(INT_MIN)); printf(" INT_MIN + 1 = %d\n", INT_MIN+1); printf("abs(INT_MIN + 1) = %d\n\n", abs(INT_MIN+1)); // Using standard library works.. printf("fabs(INT_MIN) = %.0f", fabs(INT_MIN)); return 0; }
The WP plugin forms the necessary proof obligations to ensure the program meets its
specification, simplifies them using a module called Qed
, then asks a prover such as
AltErgo
whether the obligation is provable or not. Sometimes a property is not
verified for two possible reasons:
abs
main()
code block.@ requires INT_MIN < val;
to the top of the function contract.F9
.Notice that our absolute value function has two disjoint behaviours –depending on whether the input is positive or negative. Each behaviour has some assumptions and some conclusions. Moreover, our behaviours are
disjoint
: Every input can only satisfy the assumptions of at most one of the behaviours.
As such, the program is ‘deterministic’: At most one of the behaviours is possible.
–The program is really a relation and this ensures it is univalent; i.e., a partial function–complete
: Every input satisfies the assumptions of at least one of the behaviours.
As such, the program is ‘total’: At least one behaviour is possible.
–The program is really a relation and this ensures it is total; i.e., defined on all
inputs–
We expressed our behaviours in the forms ensures ⟨assumptions⟩ ==> ⟨consequences⟩
.
However this can get unruly when there are many assumptions and many consequences.
Moreover, expressing disjointness is tedious and errorprone even in our little
example, below, where it becomes the assertion: (val < 0 && val >= 0) <==> \false
.
As such there is the alternative behavior
syntax –note the American spelling!
Using this syntax, we can ask WP to verify that the behaviours are complete or disjoint,
or both.
#include<limits.h> /*@ requires INT_MIN < val; @ ensures always_nonnegative: \result >= 0; @ @ behavior positive_input: @ assumes val > 0; @ ensures \result == val; @ @ behavior negative_input: @ assumes val <= 0; @ ensures \result == val; @ @ complete behaviors; @ disjoint behaviors; */ int abs(int val) { return (val < 0) ? val : val; }
Exercise Replace each FixMe so that the program is proven correct.
#include "Prelude.c" /*@ requires INT_MIN < val; @ ensures always_nonnegative: \result >= 0; @ ensures Exercise: @ FixMe // Positive case @ && (val <= 0 ==> \result == val) // Negative or 0 case @ && !(val > 0 && val <= 0) // Disjointness condition @ && FixMe // Completeness condition @ ; */ int abs(int val) { return (val < 0) ? val : val; }
Exercise Replace each FixMe with the weakest proposition so that the program is proven correct.
#include "Prelude.c" /*@ requires INT_MIN < val; @ ensures always_nonnegative: \result >= 0; @ @ behavior positive_input: @ assumes Exercise: FixMe; @ ensures \result == val; @ @ behavior negative_input: @ assumes Exercise: FixMe; @ ensures \result == val; @ @ complete behaviors; // FramaC complain here, but please fix the “Exercises”! */ int abs(int val) { return (val < 0) ? val : val; }
Exercise Replace each FixMe with the strongest proposition so that the program is proven correct.
#include "Prelude.c" /*@ requires INT_MIN < val; @ ensures always_nonnegative: \result >= 0; @ @ behavior positive_input: @ assumes Exercise: FixMe; @ ensures \result == val; @ @ behavior negative_input: @ assumes Exercise: FixMe; @ ensures \result == val; @ @ disjoint behaviors; */ int abs(int val) { return (val < 0) ? val : val; } // This program passes 100%, but that is because it assumes false, the “FixMe”.
In this section we step back a little to get more comfortable with requires
preconditions and ensures
postconditions. Moreover, we use this time to remind
ourselves of some elementary logic. After all, we use logic to express properties
and hope FramaC can verify them.
In some sense false, true behave for the 𝔹ooleans as ∞, +∞ behave for the ℕumbers.
𝔹ooleans  ℕumbers 
p ⇒ true  n ≤ +∞ 
false ⇒ p  ∞ ≤ n 
Implication ⇒  Inclusion ≤ 
Conjunction ∧  Minimum ↓ 
Disjunction ∨  Maximum ↑ 
Using this correspondence we can rephrase the “Golden Rule” p ∧ q ≡ p ≡ q ≡ p ∨ q as the following trivial property x ↓ y = x ≡ y = x ↑ y –“The minimum of two numbers is the first precisely when the second is their maximum.” Neat Stuff!
Ex falso quodlibet From false, anything follows: false ⇒ p, for any p.
Edit FixMe
in the following snippet so that it ensures the result is equal to 42.
#include "Prelude.c" /*@ requires uhOh: a < 0 && a > 0; @ ensures what: Exercise: FixMe; */ int id(int a){ return a;}
Right Identity of Implication Everything implies true; that is p ⇒ true, for any p.
Edit FixMe
in the following snippet so that it there are no errors.
#include "Prelude.c" /*@ requires Exercise: FixMe; // < Change this false positive. @ ensures \true; */ int id(int a){ return a; }
Exercise
Replace each FixMe
with the weakest possible predicate so that it passes.
#include "Prelude.c" /*@ requires \true; @ ensures UpperBound: a <= \result && b <= \result; @ ensures Exercise: Selection1: FixMe ==> \result == b; @ ensures Exercise: Selection2: FixMe ==> \result == a; @*/ int max(int a, int b) { return a < b ? b : a; }
Conversely, that maximum is the least upper bound, x ≤ z ∧ y ≤ z ≡ x ↑ y ≤ z, corresponds to the characterisation of disjunction (p ⇒ r) ∧ (q ⇒ r) ≡ (p ∨ q) ⇒ r –incidentally this is also known as “case analysis” since one proves p ∨ q ⇒ r by providing a proofs that if p ∨ q is true due to p then with p in hand we need to show r, and likewise if p ∨ q is true due to q.
Exercise
Replace FixMeCode
with the least amount of code so that the following passes.
#include "Prelude.c" #define max(a,b) (a < b ? b : a) // Ignore me. /*@ requires max(a, b) <= c; @ ensures a <= c && b <= c; @*/ void case_analysis(int a, int b, int c) { FixMeCode; }
Since function calls may alter memory state, the computation of a term may now
not only produce a value, as before, but also alter the state altogether.
( Foreshadowing: The assigns
ACSL keyword! )
Since a return
interrupts executation, the sequence computation rule
wp (S₁;S₂) = wp S₁ ∘ wp S₂
is no longer valid when the execution of S₁
causes the
execution of a return
thereby necessitating that S₂
is not executed.
As we have already seen, we keep the rule valid by simply defining wp U R ≡ true
for any unreachable code U
–as is S₂
when S₁
has a return.
That is, unreachable assertions are always ‘true’: They are never in a memory state, and so cannot even be evaluated, let alone be false!
int main() { goto End; //@assert my_cool_nonsense: 0 == 1; // This is unreachale but ‘true’. End: return 0; }
Likewise with infinite loops,
int main() { while(1 > 0); //@assert my_cool_nonsense: 0 == 1; // This is unreachale but ‘true’. return 0; }
That is, FramaC considers ‘partial correctness’: A specification is satisfied, provided it terminates.
In addition, since imperative expressions can modify memory, considerations must be
given to the fact arguments of a function are evaluated from left to right.
For example, suppose x,y
are imperative constructions yield integers, then
x + y
and y + x
are not guaranteed to produce the same behaviour!
#include<stdio.h> // for IO int f(){ printf("\nf(): Hello with Four!"); return 4;} int g(){ printf("\ng(): Hello with Three!"); return 3;} int main() { int result; // The output to the screen changes, // even though the *value* of result does not. result = f() + g(); printf("\n"); result = g() + f(); return 0; }
The C language does not specify the order of evaluation of function arguments –albeit it is usually lefttoright–, and it is up to the programmer to write programs whose result does not depend on the order of evaluation.
Exercise: Produce a FramaC checked variant of this example.
Remember to remove all printf
's!
Applying the definition of wp
to the body of the following swap
gives us
wp swap = id
, thereby demonstrating that this swap
does not change the
values of two variables!
void swap(int a, int b){ int c; c = a; a = b; b = c; }
The default mechanism of argument passing is that of pass by value:
Only values are sent to function bodies, which cannot alter the original variables.
Indeed, what should swap(x+y, 2)
perform if it were to “alter the given variables”?
To say that two distinct variables share the same location in memory requires us to formally introduce a notion of location that variables may reference. Rather than introduce a new such type, C makes the convention that certain numeric values act –possibly dual roles– as reference locations.
Hence we can associate variables to references which are then associated to values. That is, a state now consists of two pieces: An environment mapping variables to references and a memory state mapping references to values. The key insight is that the environment may be noninjective thereby associating distinct variables to the same reference thereby permitting them to alter the shared value. Incidentally, the shared value can be thought of as a buffer for message passing between the two variable agents. Neato!
In C, passing by reference is not a primitive construct, but it can be simulated.
The type of references that can be associated with a value of type T
in memory
is written T*
in C. Incidentally, the dereference operator is written *
in C.
For example, in environment u ↦ r₁
and memory state r₁ ↦ r₂, r₂ ↦ 4
we have
that u
has value r₂
whereas *u
has value 4
. That is, u
is a reference value at location r₁
having contents r₂
, which when dereferenced refer to the value contained in location r₂
which is 4.
The reference associated with variable x
in a C environment is written &x
.
E.g., in environment x ↦ r
and memory state r ↦ 4
, the value of x
is the integer 4
whereas the value of &x
is the reference r
. Moreover, the value of *&x
is the integer 4.
&_ : ∀{T} → Variable T → Reference T  “address of” *_ : ∀{T} → Reference T → T  “value of”  Using ‘value equality’: Inverses: ∀ a : Var T. *&a ≈ a Inverses: ∀ r : Ref T. &(*r) ≈ r
void understanding_references() { int a = 4; // integer a refers to 4 int* x = &a; // integer reference x refers to the location of a // Facts thus far // //@ assert a_is_a_number: a == 4; //@ assert x_points_to_a: *x == a == 4; //@ assert x_is_a_location: x == &a; // The inverse law: a == *(&a). // //@ assert x_points_to_a: *x == a == 4; //@ assert x_is_a_location: x == &a; //@ assert equals_for_equals: *(&a) == *x == a; // The inverse law: &(*x) == x // //@ assert x_points_to_a: *x == a == 4; //@ assert x_is_a_location: x == &a; //@ assert equals_for_equals: &(*x) == &a == x; }
If t
is an expression of type T*
then the C language has the assignment
construct *t = u
: The reference of t
is now associated with the value
of u
. The notation alludes to this executional behaviour:
The contents of t
, i.e., *t
, now refer to the value of u
.
For example, *&x = u
has the same behaviour as the assignment x = u
.
#include<stdio.h> // for IO // x and y themselves cannot be assigned to: They're constant. // I.e., assignments “x = t” are forbidden, but “*x = t” are permitted. // This makes the compiler complain if we accidently made that assignment instead. // // However, “const int* x” works in the opposite: x=t okay, but not *x=t. // Declaration “const int* const x” prevents both types of assignments. void swap(int* const x, int* const y) { int z; z = *x; // z gets the value referenced to by x *x = *y; // the location x references now gets the value referenced by y *y = z; // the location y references now gets the value z } int main() { int x = 5, y = 10; swap(&x, &y); // Note that the function is applied to the references. printf("x = %d, y = %d", x , y); return 0; }
In C, we may look for references that do not exist: C removes from memory the reference associated with a variable when that variable is removed from the environment.
#include<stdio.h> // for IO int* f(const int p) { int n = p; return &n; // n only exists locally, // whence its reference is removed when it no longer exists. } int main() { int* u = f(5); int* v = f(10); printf("u = %d, v = %d", *u , *v); // Segmentation fault! return 0; }
The compiler gives us
warning: function returns address of local variable [Wreturnlocaladdr].
We may thus turn on that warning –and all warnings really!– so that it becomes
an error at compile time.
Since we used a reference that is not declared in memory, C does not produce a compile error but the runtime result is unpredictable. Execute the above snippet to see different kinds of segmentation fault codes.
The \old
function is a builtin logical operation of ACSL.
It can only be used in the postcondition and it denotes the value before execution
of the method body. If we want to access the value at a particular memory state,
we simply refer to a label at that time frame using the \at
construct –see below.
/*@ requires \valid(a); @ ensures *a == \old(*a); @ ensures \at(*a, Post) == \at(*a, Pre); // Alternative way to say the same thing. */ void at_example(int *a) { int tmp = *a; AfterLine1: *a = 23; AfterLine2: *a = 42; AfterLine3: *a = tmp; // We are now in the memory state after the fourth line. // // Here are some true facts about the memory states: //@ assert *a == \at(*a, Pre); // Current value of *a is same as before method. //@ assert \at(*a, Here) == \at(*a, Pre); // More explicitly. //@ assert 42 == \at(*a, AfterLine3); //@ assert 23 == \at(*a, AfterLine2); }
Besides userdefined labels, \at
can also be used with the builtin labels:
Pre
: Value before function call.Post
: Value after function call. –Can only be used in the postcondition.Here
: Value at the current program point. –This' the default for standalone variables.
Whereas \old
can only be used in the postcondition, \at
can be used anywhere.
Notice that we used the builtin \valid
to ensure that access to pointers is safe
–i.e., pointing to a real memory location– thereby avoiding runtime errors.
We may also write \valid(p + (l .. u))
to express the validity of pointers
p + i
for l ≤ i ≤ u –this will be helpful when working with arrays.
Moreover, when working with constant, nonmutable, pointers, or if we wish to
be more accurate, we may use \valid_read(p)
to express that the pointer p
is valid for
readaccess only –no writing permitted.
assigns
Since methods may alter state, thereby producing sideeffects, it becomes important
to indicate which global and local variables a method assigns to
–that way its effects are explicit. We use the assigns
clause to declare this.
Unless stated otherwise, WP assumes a method can modify anything in memory;
as such, the use of assigns
becomes almost always necessary.
When a method has no sideeffects, thereby not assigning to anything, we may
declare assigns \nothing
.
/*@ requires \valid(a) && \valid(b); // @ assigns *a, *b; @ ensures *a == \old(*b); @ ensures *b == \old(*a); */ void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; }
Notice that the following block fails to prove all goals
–comment out the assigns
clause below and recheck … still no luck!
This can be fixed by uncommenting the assigns
clause above.
int h = 12; // new global variable! //@ assigns \nothing; // In particular, this method does not alter “h”. int main() { int a = 1; int b = 2; //@ assert h == 12; swap(&a, &b); //@ assert h == 12; return 0; }
Finally it is to be noted that assigns
do not occur within a behavior
–it occurs before by declaring all variables that may be altered, then
each behavior
would include a clause for the unmodified variables by indicating
their new value is equal to their \old
one.
separated
The raison d'être of pointers is to be able to have aliases for memory locations. When the pointers refer to simple data, we may act functionally in that we copy data to newly allocated memory locations. However, sometimes –such as when we program with linked lists– copying large amounts of data is unreasonable and we may simply want to alter given pointers directly. When the given pointers are identical then an alteration to one of them is actually an alteration to the rest!
When we program with lists, we shall see that if we catenate two lists by altering the first to eventually point to the second then it all works. However, if we catenate a list with itself then the resulting alteration is not the catenation of the list with itself but instead is an infinite cycle of the first list! –We'll see this later on.
Here is a simpler example,
#include<limits.h> /*@ requires \valid(fst) && \valid_read(snd); @ requires no_flow: INT_MIN <= *fst + *snd <= INT_MAX; // @ requires \separated(fst, snd); @ assigns *fst; @ ensures uhOh: *fst == \old(*fst) + *snd; */ void increment_first_by_second(int *fst, int const *snd) { *fst += *snd; }
Notice since we're only assigning to *fst
, we need not explicitly state
ensures *snd == \old(*snd)
. However, in the event that fst
and snd
both point
to the same memory location, then we actually are assigning to both!
As such the final ensures
is not necessarily true either!
We need to uncomment the separated
declaration: The memory locations are distinct.
Notice that in the final call below, since the precondition to
increment_first_by_second
fails, we have breached its contract and therefore
no longer guarenteed the behaviour it ensures
.
Since the contract does not tells what happens when we breach it, anything is possible
and so anything is “true”!
int main() { int life = 42, universe = 12; int *this = &life; int *new = &universe; //@ assert *this == 42 && *new == 12; increment_first_by_second(this, new); //@ assert *this == 42 + 12 && *new == 12; // Yay! int *that = &life; // uhoh! //@ assert *this == 54 && *that == 54; increment_first_by_second(this, that); //@ assert *this == 54 + 54 && *that == 54; // Nope...? //@ assert 1 == 0; // Notice everything is now “true”! return 0; }
We may invoke \separated(p₁, …, pₙ)
to express the pointers pᵢ
should refer to distinct
memory locations and therefore are nonoverlapping.
As a matter of abstraction, or separation of concerns, a program may be split up into an interface of declarations –a ‘header’ file– and an implementation file. FramaC permits this approach by allowing us to use a specification of a declared method, which it assumes to be correct, and so we need to verify its precondition is established whenever we call it. In some sense, for proof purposes, this allows us to ‘postulate’ a correct method and use it elsewhere –this idea is very helpful when we want to use an external libary's methods but do not –or cannot– want to prove them.
#include<limits.h> /*@ requires val > INT_MIN; @ assigns \nothing; @ ensures always_non_negative: \result >= 0; @ ensures non_negative: 0 <= val ==> \result == val; @ ensures negative: val < 0 ==> \result == val; @ */ int abs(int val); /*@ assigns \nothing; @ ensures UpperBound: a <= \result && b <= \result; @ ensures Selection1: a <= b ==> \result == b; @ ensures Selection2: b <= a ==> \result == a; @*/ int max(int a, int b); // Uncomment this to observe proof obligations failing. // // int max(int a, int b){ return 5; } /*@ requires inherited_from_abs: a > INT_MIN && b > INT_MIN; @ assigns \nothing; @ ensures always_non_negative: inherited_from_abs: \result >= 0; @ ensures upper_bound: inherited_from_max: \result >= a && \result >= a // ≈ result ≥ a && \result >= b && \result >= b; // ≈ result ≥ b @ ensures selection: inherited_from_max: \result == a  \result == a  \result == b  \result == b; */ int abs_max(int a, int b) { return max(abs(a), abs(b)); }
If we press F8
, the framac gui shows green bullets for the declarations'
specifications. They have no implementation and so are assumed to be true.
Then the abs_max
operation ‘inherits’ the preconditions and postconditions of
the methods it calls along the variables it uses.
Thus far we have only considered builtin types, we now turn to considering userdefined types that are more complex and are composites of simpler types by using the record construct.
A tuple x = (x₀, x₁, …, xₙ)
is a function over the domain 0..n
, but in programming
the domain is usually of named fields, labels, and then it is called a record.
E.g., {name = "Jasim", age = 27, language = "ar"}
is a record.
In the case that the labels are numbers, we obtain the notion of an array.
In other languages, this may be known as a class
.
In Haskell this is the data
keyword with ‘record syntax’,
and in Agda we may go on to use the record
keyword.
C records are known as structures and they are just a list of
labels with their types. In using struct
types, the struct
keyword must precede the type name in all declarations.
struct Pair { int fst; int snd; }; // Note that “struct” always precedes the type name “Pair”. // Structs are passed in by value: They are copied locally. // //@ assigns \nothing; void doesNothing(struct Pair p) { p.fst = 666; } // As usual, we use pointers to pass values by reference. // /*@ requires \valid(p); @ assigns (*p).fst; */ void woah(struct Pair* p) { (*p).fst = 313; }
#include<stdio.h> // for IO int main() { struct Pair p; // no initalisation // Composite type without a name. struct {int one; int two;} q; // Initialisation with declaration. struct Pair r = {11, 13}; // Note that in C, noninitialised variables have “arbitrary” values // which may change according to each new compilation! printf("\n p = ⟨fst: %d, snd: %d⟩", p.fst, p.snd); // Set only first field. p.fst = 3; printf("\n p = ⟨fst: %d, snd: %d⟩", p.fst, p.snd); // Zeroout all fields. struct Pair s = {}; printf("\n s = ⟨fst: %d, snd: %d⟩", s.fst, s.snd); // Invoke functions doesNothing(s); printf("\n s = ⟨fst: %d, snd: %d⟩", s.fst, s.snd); woah(&s); printf("\n s = ⟨fst: %d, snd: %d⟩", s.fst, s.snd); return 0; }
null
.null
or another reference.
To associate a record with a variable, we need to create memory large enough to contain the record
contents. Some languages use the keyword new
to accomplish this task: Create a new reference
and associated it with the record variable being defined.
For example, in Java,
class Pair { int fst; int snd; } Pair p = new Pair();
The resulting environment is p ↦ r
and the resulting memory state is
r ↦ r', r' ↦ {fst = 0, snd = 0}
. Note that default values are used.
new
is called a cell.
Interestingly in C the creation of records does not allocate cells and so there
is no need for the new
keyword. Indeed record variables cannot ever have the value null
.
C directly associates a variable with a reference which has the record contents as its value.
That is, C has one less level of indirection than is found in Java.
E.g., struct Pair p = {2, 3};
gives us environment p ↦ r
and memory state r ↦ {fst = 2, snd = 3}
,
whereas Java would have p ↦ r′
in the environment and r′ ↦ r
additionally in the memory state
That is to say, records in Java correspond to references to records in C
and so Java access notation r.f
is rendered in C as (*r).f
.
Remember that references are themselves firstclass values and it is possible for a reference to be associated to another reference.
Records are usually handled using four constructs:
class
or struct
.new
or malloc
.myRecord.myField
.myRecord.myField = myValue
.
Understanding records in a language is thus tantamount to understanding
these fundamental basics. E.g., in functional languages, assignment to a field
is essentially record copying or, more efficiently, reference redirection.
Incidentally, neither Haskell nor Agda make use of the new
keyword:
Declarations must be accompanied by initialisation which indicate the need
for cell allocation –consequently there is no need for a null
value.
The use of new
may be used for careful efficiency optimisations,
or memory management –which is rarely brought to the forefront in functional languages.
The act of assigning to each field of a record is so common that they are usually placed into a socalled constructor method.
Suppose x
and y
are variables of type Pair
, with environment and memory state:
locations = x ↦ r₁, y ↦ r₂ values = r₁ ↦ r₃, r₂ ↦ r₄, r₃ ↦ {fst = 3, snd = 5}, r₄ ↦ {fst = 7, snd = 9}
Then assignment y ≔ x
results in:
locations = x ↦ r₁, y ↦ r₂ values = r₁ ↦ r₃, r₂ ↦ r₃, r₃ ↦ {fst = 3, snd = 5}, r₄ ↦ {fst = 7, snd = 9} Change Here
That is, the value of x
is computed which is the reference r₃
–since value (location x) ≈ value r₁ ≈ r₃
–
and we associate this value with the location of y
, that is, reference r₂
.
Notice that now no variable has the value of r₄
and so it is considered garbage
in our state. Moreover, nothing can get to it since values are only associated with
locations, none of which have address r₄
. Hence there is no observable affect to
their absence or presence. We want to recycle the physical memory
or else we would quickly run out of memory. A garbage collector is an automated
system that collects and recycles such cells. Older languages like C do not
have such a system and so memory must be managed by hand.
Anyhow, henceforth x
and y
share the values of the record thereby all alterations,
through either variable, are observable by the other.
Since x
and y
are reference values, the assignment makes x == y
a true statement
since they share the same cell. This is known as physical equality.
Sometimes we wish for two variables to share a single integer, which may
not be possible with builtin types, but can be accomplished by using wrapper record types:
Records that have a lone single field. This idea of `boxing up' primitive types
allows us, for example, to define functions with arguments that are passed by reference
thereby modifying their arguments; such as the swap
function that swaps the contents of its
arguments.
If we instead execute y.fst ≔ x.fst; y.snd ≔ x.snd
Then the resulting state is:
locations = x ↦ r₁, y ↦ r₂ values = r₁ ↦ r₃, r₂ ↦ r₄, r₃ ↦ {fst = 3, snd = 5}, r₄ ↦ {fst = 3, snd = 5} Change Here
In this case, any alteration to x
's values are not observable by y
.
Moreover, in this case, all fields are equal and so we have x
and y
are structurally equal. This notion is sometimes called equals method
and the previous is equals equals (==)
.
Arrays are essentially records whose labels are numeric and all labels have the same type. The number of fields of an array is determined during allocation of the array, and not during the declaration of its type, as is the case with records. This tradeoff makes arrays more desirable in certain contexts.
The fields are usually numbered 0
to n1
, where n
is the number of fields.
Once an array is allocated, its size cannot be changed. –Stop & think: Why not?
T
are denoted by T[]
in C/Java.
T[][]
with access
by T[i][j]
.C arrays, like records, are not allocated. Consequently, their size cannot be determined by allocation and so must be a part of their type.
C arrays of type T
of length n
are declared using the mixfix syntax: T x[n];
Each element of the array is arbitrary –with no designated defaults–
so it is best to initialise them. E.g., int x[10] = {};
sets all elements of
the array to 0.
#include<stdio.h> // for IO int makeFive(int x[], int length) { for(int i=0; i != length; i++) x[i] = 5; } int main() { int x[3] = {}; printf("\n x = [%d, %d, %d]", x[0], x[1], x[2]); makeFive(x, 3); printf("\n x = [%d, %d, %d]", x[0], x[1], x[2]); return 0; }
However, C arrays differ from C records in that array variables are actually references that are associated in memory with an array. Consequently, array arguments to methods are automatically pass by reference!
Moreover this means the assignment t[k] = u
works in a rather general sense:
t
suffices to be any expression whose value is a reference associated in memory
with an array.
\forall, \exists
Arrays are commonly handled using loops; let's look at some examples.
/*@ requires 0 < N; @ requires \valid_read(array + (0 .. N  1)); // N is the length of the array @ @ assigns \nothing; @ @ behavior found_element: @ assumes \exists integer i; 0 <= i < N && array[i] == element; @ ensures 0 <= \result < N; @ @ behavior did_not_find_element: @ assumes \forall integer i; 0 <= i < N ==> array[i] != element; @ ensures \result == N; @ @ disjoint behaviors; @ complete behaviors; */ int linear_search(int* array, int N, int element) { int n = 0; /*@ loop assigns n; @ loop invariant 0 <= n <= N; @ loop invariant not_found_yet: \forall integer i; 0 <= i < n ==> array[i] != element; @ loop variant N  n; */ while( n != N && array[n] != element ) n++; return n < N ? n : N; }
Some remarks are in order.
\valid_read(array + (0 .. N  1))
ensures that the memory addresses
array + 0, ..., array + N1
can be read –as discussed when introducing \valid
.forall
syntax.n, N
is intended to be suggestive:
When n = N then we have traversed the whole array.
integer
is preferable to the C type int
since it is not constrained by
any representation limitations and instead acts more like its pure mathematical counterpart ℤ.
It is important to note that the universal ‘∀’ uses ==>
to delimit the
range from the body predicate, whereas the existential ‘∃’ uses &&
–this observation is related to the “trading laws” for quantifiers.
\forall type x; r(x) ==> p(x) 
Every element x in range r satisfies p 
\exists type x; r(x) && p(x) 
Some element x in range r satisfies p 
Notice the striking difference between
the \exists integer x; \false && even x
–which is unprovable since false is never true!–
and \exists integer x; \false ==> even x
–which can be satisfied by infinitely many x
, since false implies anything.
Of course we could start at the end of the array and “work down” until
we find the element, or otherwise, say, return 1. Let's do so without using
a new local variable n
.
#include "Prelude.c" /*@ requires 0 < N; // @ requires Exercise: read access to a[0], ..., a[N1]; @ // @ assigns FixMe; @ @ behavior found_element: @ assumes Exercise: FixMe; // “element ∈ array” @ ensures Exercise: FixMe; // Output is valid index in array @ @ behavior did_not_find_element: @ assumes Exercise: FixMe; // “element ∉ array” @ ensures \result == 1; @ @ disjoint behaviors; @ complete behaviors; */ int linear_search_no_local(int* array, int N, int element) { /*@ loop assigns N; @ loop invariant 0 <= N <= \at(N, Pre); // @ loop invariant not_found_yet: Exercise: FixMe; // @ loop variant Exercise: FixMe: 666; */ while( 0 != N && array[N1] != element ) N; return N  1; }
#include "Prelude.c" /*@ requires Exercise: FixMe; // array[0], ..., array[N1] are accessible @ requires element < INT_MAX; @ assigns array[0 .. N1]; @ @ ensures Exercise: all_array_equals_element: FixMe; */ void make_constant(int* array, int N, int element) { /*@ loop assigns N, array[0 .. \at(N,Pre)1]; @ loop invariant range_on_N: FixMe; @ loop invariant constant_so_far: FixMe; @ loop variant N; */ for (; 0 != N; N) array[N1] = element; }
Notice that the invariants are getting way too long –and worse: Repetitive! We can abstract common formulae into more general and reusable shapes by declaring them as ACSL logical functions –keep reading!
On an unrelated note, sometimes we try to prove a program that we just coded incorrectly, so if things are going nowhere then maybe try a few tests to ensure you're on the right track.
The definition of wp
for function calls is correct provided the function body
itself only contains invocations to previously defined functions?
What about recursive function definitions: Definitions invoking the function being defined or invoking functions that invoke functions that eventually invoke the function currently being defined?
Since invocations are delegated to the state, which handles all defined names, we may invoke whatever function provided its name is found. Since C requires names to be declared before use, we may have mutually recursive functions by ‘prototyping’: Declaring the function signature, then at some point providing the actual implementation.
#include<stdio.h> // for IO #include<stdbool.h> bool odd(int); // protoyping the odd function bool even(int n) // using odd here, even though it's not yet defined { if (n == 0) return true; else return odd(n  1); } bool odd(int n) { if (n == 0) return false; else return even(n  1); } int main() { printf("\n 7 is even? ... %d", even(7)); printf("\n 7 is odd? ... %d", odd(7)); return 0; }
Anyhow, e.g., void f(int x){ f(x); }
has the definition of wp f(x)
using the value of wp f(x)
and so is circular. How can this be avoided?
Note that a recursive definition is not just a definition that uses the object which it
is defining. Otherwise, the previous f
might as well have been the definition of
the factorial function that on input x
simply invokes itself; then again it could
have been any function!
We generally think of recursive definitions as definitions by usual ℕinduction, however
this is not absolutely true.
E.g., the following function at n
not only relies on
values on smaller inputs but also on values at larger inputs to compute the value at n
!
#include<stdio.h> // for IO int iterations = 0; #define RETURN(i, n) { printf("\n **Computed value for input %d is %d**", i, n); \ iterations++; return n; } // Uses smaller as well as larger values just to compute current value! int f(int n) { printf("\n Computing value for: %d", n); if (n <= 1) RETURN(n, 1) if (n % 2 == 0) RETURN(n, 1 + f(n / 2)) else RETURN(n, 2 * f(n + 1)) } int main() { f(12); printf("\n\n The function was invoked for a total of %d times!", iterations); return 0; }
Similarly the Ackermann function is a recursive function that has been proven to be undefinable using only nested definitions by ℕinduction. ( However, since ℕ×ℕ is wellordered, it is a valid definition. )
We may try to remove recursive calls by replacing every recursive call with the function body itself, which then has a new recursive call. We may continue to expand forever by replacing recursive calls with the original function body. The “result” will be an nonrecursive program that is infinitely long.
Thus, recursive programs, like while
loops, are a means of expressing infinite
programs and, like while
loops, they introduce the possibility of nontermination.
As for while
loops, we can approximate the infinitely long nonrecursive program
by simply givingup on the n
th expansion, not making any more recursive calls.
Essentially, we do n
recursive calls then giveup if the program has not completed.
Hence, we again consider the limit.
A recursive function such as the factorial function can be seen as
an equation where the unknown variable is the function currently being defined.
For example, the factorial function is the unique partial function f
satisfying
the equation
f ≈ (x ↦ if x ≈ 0 then 1 else x * f(x1))
This equation has the form f ≈ F(f)
, so it is a “fixed point equation”.
Since recursive functions may not terminate, the functions they describe
are partial. It can be proven that any fixed point equation always has
at least one solution; i.e., it defines at least one partial function on the integers.
For example, the equation f ≈ (x ↦ 1 + f(x))
corresponding to
int loop(int n){ return 1 + loop(n); }
Has one solution: The empty function, which is not defined on any input.
The set of possible solutions can be ordered by inclusion of their graphs and so one of them is the smallest. Incidentally, this least solution is also obtained by the aforementioned limit!
E.g., every function is a solution to the equation f ≈ (x ↦ f x)
.
Notice that the factorial function can be written without using assignments:
/*@ axiomatic Factorial @ { @ logic integer factorial(integer n); @ @ axiom fact_zero: \forall integer n; factorial(0) == 1; @ @ axiom fact_succ: \forall integer n; 0 <= n @ ==> factorial (n + 1) == n * factorial (n); @ @ axiom fact_monotone : \forall integer m,n; 0 <= m <= n @ ==> factorial(m) <= factorial(n); @ } */
#include "Prelude.c" /*@ requires Exercise: FixMe; @ assigns \nothing; @ ensures Exercise: FixMe; */ int fact_imp(int n) { int r = 1; /*@ loop assigns i, r; @ loop invariant range_on_i: FixMe; @ loop invariant relationship_between_r_i_n: FixMe; @ loop variant FixMe: 666; */ for(int i = 1; i <= n; i++) r = r * i; return r; } // ≈ /*@ requires 0 <= n; @ requires factorial(n) <= INT_MAX; @ assigns \nothing; @ ensures \result == factorial(n); */ int fact_functional(int n) { if (n == 0) return 1; return n * fact_functional(n  1); }
( Notice that ¬(i ≤ n)[i ≔ 1] ≡ n = 0 when considering n : ℕ. )
Hence if we remove assignments, then the memory state for computation is always empty and so sequences and loops become useless. We are left with only variable declarations, function calls, and the builtin operations. This is the functional core of the language and it is surprisingly as powerful as the imperative core. Why? Recall that a term, or statement, can be thought of as a (partial) function of its free variables; now the functions obtained this way using only the functional core are the same as those obtained by also using the whole of the imperative core! Note that omitting recursion falsifies this result.
We have now covered enough material to tackle the problem posed
at the very beginning —that of whatDo
.
#include "Prelude.c" /*@ requires \valid(x) && \valid(y); requires 0 <= *x < 31; requires Exercise: FixMe; assigns *x, *y; ensures Exercise: FixMe; */ void whatDo(int* x, int* y) { if (*x == 0) { *y = 1; *x = 3; } else { *x = 1; *y = 7; whatDo(x, y); *y *= 2; *x = 5; } }
Running a few test inputs, it can be seen that this program
sets y
to be the power of x
. Further testing may reveal interesting
issues when x
and y
refer to the same memory location!
⟨x = 0, y = 0⟩ ↦ ⟨x = 3, y = 1⟩ ⟨x = 1, y = 2⟩ ↦ ⟨x = 5, y = 2⟩ ⟨x = 3, y = 4⟩ ↦ ⟨x = 5, y = 8⟩ ⟨x = 5, y = 6⟩ ↦ ⟨x = 5, y = 32⟩ ⟨x = 7, y = 8⟩ ↦ ⟨x = 5, y = 128⟩ ⟨x = 0, y = 99⟩ ↦ ⟨x = 3, y = 1⟩ x == y ⇒ Segmentation Fault
With this guidance in hand, we aim to axiomatise the power function:
/*@ axiomatic Pow @ { @ logic integer pow(integer b, integer n); @ @ axiom pow_zero: \forall integer b; pow(b, 0) == 1; @ @ axiom pow_one: \forall integer b; pow(b, 1) == b; @ @ axiom pow_homomorphism: \forall integer b, m, n; @ pow(b, m + n) == pow(b, m) * pow(b, n); @ @ axiom pow_monotone : \forall integer b,m,n; b >= 0 && 0 <= m <= n @ ==> pow(b,m) <= pow(b,n); @ } */
Notice that there are infinitely many solutions f
to the equations
pow_zero
: f(0) = 1pow_homomorphism
: f(m + n) = f(m) * f(n)
Which ones? Since every natural number is of the form 1 + 1 + ⋯ + 0
the second requirement yields \\ f(n) = f (1 + 1 + ⋯ + 0) = f 1 * f 1 * ⋯ f 1 * f 0;
which by the first requirement simplifies to f(n) = (f 1)ⁿ.
Hence for any choice of number f(1) : ℕ, we obtain a function f
that satisfies these definitions. If we want f to be the n product of a number
b then we need to insist f 1 = b –which is just pow_one
!
From the axioms, we obtain some useful lemmas.
//@ lemma pow_succ: \forall integer b, n; n >= 0 ==> pow(b, n + 1) == pow(b, n) * b; //@ lemma powNonNeg : \forall integer b,n; b >= 0 ==> n >= 0 ==> pow(b,n) >= 0; //@ lemma pow2bound : \forall integer n; 0 <= n < 31 ==> pow(2, n) < INT_MAX; /*@ lemma powPredT : \forall integer b,n,m; b >= 0 && n > 0 && pow(b, n) <= m ==> b * pow(b, n1) <= m; */
With this setup, the reader should now be able to solve the FixMe
's in whatDo
–which I've renamed to “hehner”, after the fellow who showed it as an example
in his excellent specifications and correctness text, A Practical Theory of Programming.
#include "Prelude.c" /*@ requires \valid(x) && \valid(y); requires 0 <= *x < 31; requires Exercise: FixMe; // Assuming \false, gives us anything! assigns *x, *y; ensures Exercise: FixMe; */ void hehner(int* x, int* y) { // Introduce a local for reasoning, to avoid having to write \at(*x, Pre). // ghost const int X = *x; // assert given: 0 <= X < 31; // assert taking_powers_with_monotonicity: 1 <= pow(2,X) <= INT_MAX; if (*x == 0) { // assert condition: X == 0; // assert taking_powers: pow(2,X) == 1; *y = 1; *x = 3; // assert *y == pow(2,X); } else { // assert condition: 0 < X < 31; // assert pow_with_monotonicity: 1 < pow(2, X) <= INT_MAX; // assert factoring: 2 * pow(2, X1) <= INT_MAX; *x = 1; *y = 7; hehner(x, y); // assert function_ensures: *y == pow(2, X  1); // assert pow(2, X) == 2 * pow(2, X  1); // assert pow(2, X ) ≤ INT_MAX; // assert 2 * pow(2, X  1) ≤ INT_MAX; // assert y2_no_overflow: *y * 2 ≤ INT_MAX; *y *= 2; *x = 5; // assert our_goal: *y == pow(2, X); // assert incidentally: *y <= INT_MAX; } }
I've left the assert
's since they may make the program proof more comprehensible
to the reader –yet notice that I did not use @
and so they are not visible,
nor necessary, to FramaC.
This is what I have so far, yet I look forward to including material utilising linked lists as well as making more of the CurryHoward Correspondence.
Anyhow, I hope you've enjoyed yourself and hopefully learned something neat! Byebye!
The following utilities are loaded when this file is opened.
After the first time the file InteractiveWayToC.el
is created and this section
may be deleted, or COMMENT
ed, as it is loaded in the footer
section at the end of this file.
f7
.
f6
if there is a main
method.f9
, within Emacs.f8
.Note: There is a 10 second time limit on the proof search.
Every source block is in ‘focus’ when the variables NAME
and NAMECode
refer to it.
(setq Language "c" LanguageExtension "c") (setq NAMEEXT (buffername) NAME (filenamesansextension NAMEEXT)) (setq NAMECode (concat NAME "." LanguageExtension))
We explicitly change focus using [not]currentlyworkingwith
methods.
(defun buffernamesansextension () "" (filenamesansextension (buffername)) ) (defun currentlyworkingwith (&optional name) "Provide the name (without extension) of the source block's resulting file. The name is then tied to the “NAMECode” global variable utilised by the “showcode” method, <f7>, and the “interpret” command's global variable “NAME”. If no argument is provided, we use “⟪BufferName⟫.c” as default file name. " (unless name (setq name (buffernamesansextension))) (setq NAME name) (setq NAMECode (concat name ".c")) ) (defun notcurrentlyworkingwith (&optional name) "When “:tangle fn” has “fn” being the empty string, the tangle does not transpire. As such, it is as if we are not actually generating the source block. This operation only returns the empty string and does not alter any existing state. If we alter state, then earlier invocations to (currentlyworkingwith) are rendered useless! " "" ;; Our return value. )
Notice that the InteractiveWayToC.el
methods –<F6> to <F9>– target the
source block(s) with (currentlyworkingwith name)
where name
is the most
recent name. Note that the name
component need not be unique: Blocks having
the same one write to the same file.
In a new line enter <s
then at the end press TAB
to obtain a nice helloworld
template. Some code will be generated for you –free of charge–, edit it as you like.
(makevariablebufferlocal 'orgstructuretemplatealist) (setq TEMPLATE (concat "#+NAME: ????  goal of this code block" "\n#+BEGIN_SRC " Language " :tangle (currentlyworkingwith \"HelloWorld\") \n" "#include<stdio.h> // for IO" "\nint main() \n{\n printf(\"Hello, World!\");\n return 0; \n}" "\n#+END_SRC")) (addtolist 'orgstructuretemplatealist `("s" ,TEMPLATE))
Then to see the code generated by this file press Mx
then enter showcode
then enter;
alternatively, press Cx Ce
after the final parenthesis: (showcode).
My definition of (showcode)
begins with closing the code buffer if it exists,
then continue by extracting the most recent code and displaying it below the current buffer.
The definition of (interpret)
is nearly the same except we switch to an output buffer,
and create it if it doesn't exist.
Note that our interpretation command is essentially the following command line invocation:
NAME=myfilename ; gcc $NAME.c o $NAME.exe ; ./$NAME.exe
(defun showcode () "Show focused source blocks in a new buffer." (interactive) (ignoreerrors (killbuffer NAMECode)) (savebuffer) (orgbabeltangle) (splitwindowbelow) (findfile NAMECode) (otherwindow 1)) ;; Since there are many generated files, let's mention the name ;; of the program file currently being executed, or proven. ;; (defun insertfocusedname () "Insert the name of the focused source blocks at the beginning of the buffer." (beginningofbuffer) (insert "================\n⟪ " NAME ".c ⟫\n================\n\n")) (defun interpret () "Execute focused source blocks in a new buffer." (interactive) (savebuffer) (orgbabeltangle) (switchtobufferotherwindow "*Shell Command Output*") (shellcommand (concat "cd " defaultdirectory "; NAME=" NAME " ; gcc $NAME.c o $NAME.exe ; ./$NAME.exe")) (insertfocusedname) ;; Go back to the literate buffer. (otherwindow 1)) (defun framac () "" (interactive) (savebuffer) (orgbabeltangle) (shellcommand (concat "framacgui " NAMECode " wp rte &"))) (defun try (this that) "" (conditioncase nil (eval this) (error (eval that))))
The framacnogui
command tries to find where an error happens by placing the cursor
near an unproven assertion. It does so by looking for the phrase false
in the shell output
buffer after the framac
program is invoked. If it cannot find it, you are placed at
the end of the buffer and, ideally but not necessarily, should see all goals have passed.
(defun framacnogui () "" (interactive) (orgbabeltangle) ;; Avoid generating proof goal statements for now. ;; (shellcommand (concat "framac " NAMECode " wp wpmsgkey=printgenerated rte")) (shellcommand (concat "framac " NAMECode " wp wptimeout 10 rte")) (switchtobufferotherwindow "*Shell Command Output*") (insertfocusedname) (hllinemode) (setq framacstate (catch 'state ;; Global variable indicating current state (dolist (elem '("Exercise" "unknown" "user error" "false" "Timeout" "Proved goals")) (beginningofbuffer) (try '(and (searchforward elem) (throw 'state elem)) 'nil) ))) ;; global variable about status (setq framacstatus (format "FramaC: %d﹪ of proof complete!" (framacprogress))) ;; Use the red colour of failure. (setfacebackground 'hlline "pink") ;; Or did we succeed? ;; We might have halted a “false” *constant* even though all goals pass. ;; ⟨ This is not an issue when proofs are not being generated. ⟩ (if (equal framacstate "Exercise") (setq framacstatus (concat framacstatus " ⟪There are holes!⟫")) (when (or (equal framacstate "Proved goals") (eq (framacprogress) 100)) (setfacebackground 'hlline "pale green") (try '(searchforward "Proved goals") t))) (message framacstatus) (minimizewindow) ;; Make current buffer roughly 3 lines in height. )
Where the framacprogress
command yields the percentage denoting the number of goals proven.
(defun framacprogress () "" (let ( (here (point)) ) (beginningofbuffer) (try '(searchforward "Proved goals:") 0) ;; (killline) (copyregionaskill (point) (pointateol)) (gotochar here) (* 100 (stringtonumber (calceval (currentkill 0)))) ))
Finally,
(localsetkey (kbd "<f6>") 'interpret) (localsetkey (kbd "<f7>") 'showcode) (localsetkey (kbd "<f8>") 'framac) (localsetkey (kbd "<f9>") 'framacnogui)
It is to be noted: I only know enough Elisp to get by.
Again: Hope you had fun!
Numbers are the lengths of lists which are the flattenings of trees which are the spannings of graphs. Unlike the first three, graphs have two underlying types of interest –the vertices and the edges– and it is getting to grips with this complexity that we attempt to tackle by considering their ‘algebraic’ counterpart: Categories.
In our exploration of what graphs could possibly be and their relationships to lists are, we shall mechanise, or implement, our claims since there will be many details and it is easy to make mistakes –moreover as a selflearning project, I'd feel more confident to make bold claims when I have a proof assistant checking my work ;)
Assuming slight familiarity with the Agda programming language, we motivate the need for
basic concepts of category theory with the aim of discussing adjunctions with
a running example of a detailed construction and proof of a free functor.
Moreover, the article contains a host of exercises
whose solutions can be found in the
literate source file. Raw Agda code can be found here.
Since the read time for this article is more than two hours, excluding the interspersed exercises, it may help to occasionally consult a some reference sheets:
Coming from a background in order theory, I love Galois Connections and so our categorical hammer will not be terminal objects nor limits, but rather adjunctions. As such, everything is an adjunction is an apt slogan for us :)
 This file has been extracted from https://alhassy.github.io/PathCat/  Type checks with Agda version 2.6.0.
( Photo by Mikhail Vasilyev on Unsplash )
Lists give free monoids \(ℒ\, A = (\List\, A, +\!+, [])\) —a monoid \(𝒮 = (S, ⊕, 0_⊕)\) is a triple consisting of a set \(S\) with a binary operation \(⊕\) on it that is associative and has a unit, \(0_⊕\). That it is ‘free’ means that to define a structurepreserving map between monoids \((\List\, A, +\!+, []) \,⟶\, (S, ⊕, 0_⊕)\) it suffices to only provide a map between their carriers \(\List\, A → S\) —freedom means that plain old maps between types freely, at no cost or effort, give rise to maps that preserve monoid structure. Moreover, the converse also holds and infact we have a bijection: \[ (ℒ\, A ⟶ 𝒮) \qquad≅\qquad (A ⟶ 𝒰\, 𝒮) \] Where we write \(𝒰\, (S, ⊕, 0_⊕) = S\) for the operation that gives us the 𝒰nderlying carrier of a monoid.
Loosely put, one says we have an ‘adjunction’, written \(ℒ ⊣ 𝒰\).
Observe that natural numbers ℕ ≅ List Unit
are a monoid whose operation is commutative.
By using different kinds of elements A
–and, importantly, still not imposing any equations–
we lose commutativity with List A
.
Then by generalising further to binary trees BinTree A
, we lose associtivity and identity
are are only left with a set and an operation on it —a structure called a ‘magma’.
This is the order that one usually learns about these inductively built structures. One might be curious as to what the next step up is in this hierarchy of generalisations. It is a noninductive type called a ‘graph’ and in this note we investigate them by comparison to lists. Just as we shifted structures in the hierarchy, we will move to a setting called a ‘category’ —such are more structured than magmas but less restrictive than monoids.
For those who know category theory, this article essentially formalises the often seen phrase “consider the category generated by this diagram, or graph”. Indeed every category is essentially a free category over a graph but with additional equations that ‘confuse’ two paths thereby declaring, e.g., that one edge is the composition of two other edges.
In our exploration of what graphs could possibly be and their relationships to lists are, we shall mechanise or implement our claims since there will be many details and it is easy to make mistakes –moreover as a selflearning project, I'd feel more confident to make bold claims when I have a proof assistant checking my work ;)
Before reading any further please ingrain into your mind that the Agda keyword
Set
is read “type”! This disparity is a historical accident.
Since the Agda prelude is so simple, the core language doesn’t even come with Booleans or numbers by default —they must be imported from the standard library. This is a pleasant feature. As a result, Agda code tends to begin with a host of imports.
module PathCat where open import Level using (Level) renaming (zero to ℓ₀ ; suc to ℓsuc ; _⊔_ to _⊍_)  Numbers open import Data.Fin using (Fin ; toℕ ; fromℕ ; fromℕ≤ ; reduce≥ ; inject≤) renaming (_<_ to _f<_ ; zero to fzero ; suc to fsuc) open import Data.Nat open import Relation.Binary using (module DecTotalOrder) open import Data.Nat.Properties using(≤decTotalOrder ; ≤refl) open DecTotalOrder Data.Nat.Properties.≤decTotalOrder  Znotation for sums open import Data.Product using (Σ ; proj₁ ; proj₂ ; _×_ ; _,_) Σ∶• : {a b : Level} (A : Set a) (B : A → Set b) → Set (a ⊍ b) Σ∶• = Σ infix 666 Σ∶• syntax Σ∶• A (λ x → B) = Σ x ∶ A • B  Equalities open import Relation.Binary.PropositionalEquality using (_≗_ ; _≡_) renaming (sym to ≡sym ; refl to ≡refl ; trans to _⟨≡≡⟩_ ; cong to ≡cong ; cong₂ to ≡cong₂ ; subst to ≡subst ; subst₂ to ≡subst₂ ; setoid to ≡setoid)
Notice that we renamed transitivity to be an infix combinator.
Let us make equationalstyle proofs available for any type.
module _ {i} {S : Set i} where open import Relation.Binary.EqReasoning (≡setoid S) public
We intend our proofs to be sequences of formulae interleaved with
justifications for how the formulae are related. At times, the justifications
are by definition and so may be omitted, but we may want to mention them
for presentational –pedagogical?– purposes. Hence, we introduce the
combinator notation lhs ≡⟨" by definition of something "⟩′ rhs
.
open import Agda.Builtin.String defnchasing : ∀ {i} {A : Set i} (x : A) → String → A → A defnchasing x reason supposedlyxagain = supposedlyxagain syntax defnchasing x reason xish = x ≡⟨ reason ⟩′ xish infixl 3 defnchasing
While we’re making synonyms for readability, let’s make another:
_evenunder_ : ∀ {a b} {A : Set a} {B : Set b} {x y} → x ≡ y → (f : A → B) → f x ≡ f y _evenunder_ = λ eq f → ≡cong f eq
Example usage would be to prove
mor G (mor F Id) ≡ mor G Id
directly by ≡cong (mor G) (id F)
which can be written more clearly as
functor F preservesidentities evenunder (mor G)
,
while longer it is also much clearer and easier to read and understand
–selfdocumenting in some sense.
Interestingly, our first calculational proof is in section 5 when we introduced an large 𝒞𝒶𝓉egory.
What's a graph? Let's motivate categories!
A ‘graph’ is just a parallelpair of maps,
record Graph₀ : Set₁ where field V : Set E : Set src : E → V tgt : E → V
This ofcourse captures the usual notion of a set of nodes V
and a set of directed and labelled
edges E
where an edge e
begins at src e
and concludes at tgt e
.
What is good about this definition is that it can be phrased in any category: V
and E
are
any two objects and src, tgt
are a parallel pair of morphisms between them.
How wonderful! We can study the notion of graphs in arbitrary categories!
—This idea will be made clearer when categories and functors are formally introduced.
However, the notion of structurepreserving map between graphs, or ‘graphmaps’ for short, then becomes:
record _𝒢⟶₀_ (G H : Graph₀) : Set₁ where open Graph₀ field vertex : V(G) → V(H) edge : E(G) → E(H) srcpreservation : ∀ e → src(H) (edge e) ≡ vertex (src(G) e) tgtpreservation : ∀ e → tgt(H) (edge e) ≡ vertex (tgt(G) e)
( The fancy 𝒢 and ⟶ are obtained in Agda input mode by \McG
and \>
, respectively. )
This is a bit problematic in that we have two proof obligations and at a first glance it is not at all clear their motivation besides ‘‘structurepreserving’’.
However, our aim is in graphs in usual type theory, and as such we can use a definition that is
equivalent in this domain: A graph is a
type V
of vertices and a ‘type’ v ⟶ v’
of edges for each pair of vertices v
and v’
.
 ‘small graphs’ , since we are not using levels record Graph : Set₁ where field V : Set _⟶_ : V → V → Set  i.e., Graph ≈ Σ V ∶ Set • (V → V)  Graphs are a dependent type!
Now the notion of graphmap, and the meaning of structurepreserving, come to the forefront:
record GraphMap (G H : Graph) : Set₁ where private open Graph using (V) _⟶g_ = Graph._⟶_ G _⟶h_ = Graph._⟶_ H field ver : V(G) → V(H)  vertex morphism edge : {x y : V(G)} → (x ⟶g y) → (ver x ⟶h ver y)  arrow preservation open GraphMap
Note edge
essentially says that mor
‘shifts’, or ‘translates’, types
x ⟶g y
into types ver x ⟶h ver y
.
While equivalent, this twopiece definition is preferable over the fourpiece one given earlier since it means less proofobligations and less constructions in general, but the same expressiblity. Yay!
Before we move on, let us give an example of a simple chaingraph. For clarity, we present it in both variations.
 embedding: j < n ⇒ j < suc n `_ : ∀{n} → Fin n → Fin (suc n) ` j = inject≤ j (≤step ≤refl) where open import Data.Nat.Properties using (≤step)
This' an example of a ‘forgetful functor’, keep reading!
[_]₀ : ℕ → Graph₀ [ n ]₀ = record { V = Fin (suc n)  ≈ {0, 1, ..., n  1, n} ; E = Fin n  ≈ {0, 1, ..., n  1} ; src = λ j → ` j ; tgt = λ j → fsuc j }
That is, we have n+1
vertices named 0, 1, …, n
and n
edges named 0, 1, …, n1
with one typing axiom being j : j ⟶ (j+1)
. Alternatively,
[_] : ℕ → Graph [ n ] = record {V = Fin (suc n) ; _⟶_ = λ x y → fsuc x ≡ ` y }
However, we must admit that a slight downside of the typed approach –the twopiece definition– is now we may need to use the following ‘shifting’ combinators: They shift, or slide, the edge types.
 casting _⟫_ : ∀{x y y’} → (x ⟶ y) → (y ≡ y’) → (x ⟶ y’) e ⟫ ≡refl = e  Casting leaves the edge the same, only type information changes ≅⟫ : ∀{x y y’} {e : x ⟶ y} (y≈y’ : y ≡ y’) → e ≅ (e ⟫ y≈y’) ≅⟫ ≡refl = ≅refl
Such is the cost of using a typedapproach.
Even worse, if we use homogeneous equality then we’d have the ghastly operator
≡⟫ : ∀{x y y’} {e : x ⟶ y} (y≈y’ : y ≡ y’) → e ⟫ y≈y’ ≡ (≡subst (λ ω → x ⟶ ω) y≈y’ e)
However, it seems that our development does not even make use of these. Lucky us! However, it is something to be aware of.
A signature consists of ‘sort symbols’ and ‘function symbols’ each of which is associated sourcesorts and a targetsort –essentially it is an interface in the programming sense of the word thereby providing a lexicon for a language. A model or algebra of a language is an interpretation of the sort symbols as sets and an interpretation of the function symbols as functions between those sets; i.e., it is an implementation in programming terms. Later you may note that instead of sets and functions we may use the objects and morphisms of a fixed category instead, and so get a model in that category.
Math  ≅  Coding 
Signature  Interface, record type, class  
Algebra  Implementation, instance, object  
Language Term  Syntax  
Interpretation  Semantics, i.e., an implementation 
Formally, onesorted signatures are defined:
open import Data.Vec using (Vec) renaming (_∷_ to _,,_ ; [] to nil)  , already in use for products :/  one sorted record Signature : Set where field 𝒩 : ℕ  How many function symbols there are ar : Vec ℕ 𝒩  Their arities: lookup i ar == arity of ith function symbol open Signature ⦃...⦄  𝒩 now refers to the number of function symbols in a signature
For example, the signature of monoids consists of a single sort symbol C
–which can be
interpreted as the carrier of the monoid– and two function symbols m , u
–which can be interpreted as the monoid multiplication and unit– with sourcetarget
sort lists ((),C) , ((C,C), C)
—some would notate this by u :→ C , m : C × C → C
.
MonSig : Signature MonSig = record { 𝒩 = 2 ; ar = 0 ,, 2 ,, nil }  unit u : X⁰ → X and multiplication m : X² → X
Generalising on monoids by typing the multiplication we obtain
the signature of categories which consists of three sort symbols O, A, C
–which can be
interepreted as objects, arrows, and composable pairs of arrows– and four function symbols
⨾ , src, tgt, id
with sourcetarget sort lists (C,A) , (A,O) , (A,O) , (O,A)
—notice that only a language of symbols
has been declared without any properties besides those of typing. If we discard C, ⨾, id
we
then obtain the signature of graphs. Without knowing what categories are, we have seen that their
signatures are similar to both the graph and monoid signatures and so expect their logics to
also be similar. Moreover we now have a few slogans,
\[\color{green}{\text{Categories are precisely typed monoids!}}\]
\[\color{green}{\text{Categories are precisely graphs with a monoidal structure!}}\]
\[\color{green}{\text{Categories are precisely coherently constructive lattices!}}\]
( The last one is completely unmotivated from our discussion, but it's a good place for the slogan and will be touched on when we look at examples of categories. )
A signature can be visualised in the plane by associating a dot for each sort symbol and an arrow for each function symbol such that the arrow has a tail from each sort in the associated function symbols source sorts list and the end of the arrow is the target sort of the sort symbol. That is, a signature can be visualised as a hypergraph.
𝒢
is an interpreation/realisation of the graph’s vertices
as sets and the graph’s edges as functions between said sets.𝒢
is nothing more than a graph morphism
𝒢 ⟶ 𝒮e𝓉
, where 𝒮e𝓉
is the graph with vertices being sets and edges being functions.
Notice that a Graph₀
is precisely a model of the graph • ⇉ •
, which consists of
two vertices and two edges from the first vertex to the second vertex.
We will return to this point ;)
Before we move on, it is important to note that a signature does not capture any constraints required on the symbols –e.g., a monoid is the monoid signature as well as the constraint that the 2arity operation is associative and the 0arity operation is its unit. More generally, a specification consists of a signature declaring symbols of interest, along with a set of sentences over it that denote axioms or constraints. In programming parlance, this is an interface consisting of types and functions that need to be provided and implemented, along with constraints that are usually documented in comments. Unsurprisingly, models of specifications also form categories.
In this section we introduce the notion of a ‘‘poorman’s category’’ along with the notion of structure preserving transformations and structure preserving transformations between such transformations. The former are called functors and the latter are known as natural transformations and are considered one of the most important pieces of the fundamentals of category theory; as such, we discuss them at length. Afterwards, we relate this section back to our motivating discussion of graphs.
A category, like a monoid, is a a few types and operations for which some equations hold.
However, to discuss equations a notion of equality is needed and rather than enforce one
outright it is best to let it be given. This is a ‘set’ in constructive mathematics:
A type with an E
quivalence relation on it —also called a setoid or an E
set.
However, then the structure must have a few added axioms: The operations must be congruences,
i.e., preserve the equivalence relation, and structurepreserving maps must also be congruences.
For our purposes we will use propositional equality and pointwise propositional equality, and as such most of the proofs fall out of the fact that propositional equality is an equivalence. However, this setoid structure becomes a bit of a noise, without providing any real insight for our uses, and the issues of equivalences will be a distraction from the prime focus. Instead, for our two cases where we use pointwise propositional, we will postulate two forms of extensionality. Without question this is not a general approach —then again, our aim is not to develop a library for category theory, which has already been done so elegantly by Kahl who calls it the RATHAgda project.
module _ where  An anyonomous module for categorial definitions record Category {i j : Level} : Set (ℓsuc (i ⊍ j)) where infixr 10 _⨾_ field Obj : Set i _⟶_ : Obj → Obj → Set j _⨾_ : ∀ {A B C : Obj} → A ⟶ B → B ⟶ C → A ⟶ C assoc : ∀ {A B C D} {f : A ⟶ B}{g : B ⟶ C} {h : C ⟶ D} → (f ⨾ g) ⨾ h ≡ f ⨾ (g ⨾ h) Id : ∀ {A : Obj} → A ⟶ A leftId : ∀ {A B : Obj} {f : A ⟶ B} → Id ⨾ f ≡ f rightId : ∀ {A B : Obj} {f : A ⟶ B} → f ⨾ Id ≡ f open Category using (Obj) open Category ⦃...⦄ hiding (Obj)  Some sugar for times when we must specify the category  “colons associate to the right” ;) arr = Category._⟶_ syntax arr 𝒞 x y = x ⟶ y ∶ 𝒞  “ghost colon” cmp = Category._⨾_ syntax cmp 𝒞 f g = f ⨾ g ∶ 𝒞  “ghost colon”
However, similar to nearly everything else in this document, we can leave the setoid approach as an exercise for the reader, which of course has solutions being in the literate source.
Moreover, lest you’re not convinced that my usage of extensionality is at all acceptable, then note that others have used it to simplify their presentations; e.g., Relative monads formalised. Such ‘appeal to authority’ is for the lazy reader who dares not think for him or herself, otherwise one ought to read up on the evils of using equality instead of equivalence relations so as to understand when one thing is really another.
The diligent reader may be interested to know that Maarten Fokkinga has written a very gentle introduction to category theory using the calculational approach; I highly recommend it!
Anyhow, in place of strict equality, one uses categorical isomorphism instead.
open Category using (Obj) public record Iso {i} {j} (𝒞 : Category {i} {j}) (A B : Obj 𝒞) : Set j where private instance 𝒞′ : Category ; 𝒞′ = 𝒞 field to : A ⟶ B from : B ⟶ A lid : to ⨾ from ≡ Id rid : from ⨾ to ≡ Id syntax Iso 𝒞 A B = A ≅ B within 𝒞
Interestingly, we shall later encounter a rather large category named 𝒞𝒶𝓉 possessing the special property of being a “2Category”: It has morphisms between objects, as expected, which are now called “1morphisms”, and it has morphisms between 1morphisms, also called “2morphisms”.
That is, it has morphisms between morphisms.
Above we argued that equality should be deferred in favour of isomorphism at the morphism level. Hence, in a 2Category, it is only reasonable to defer an equation involving objects to be up to isomorphism of 2morphisms —this is then called an “equivalence”.
ℒHS ≃ ℛHS ⇔ Σ F ∶ ℒHS ⟶ ℛHS • Σ G ∶ ℛHS ⟶ ℒHS • F ⨾ G ≅ G ⨾ F ≅ Id
Hence when it comes to categories themselves, one usually speaks in terms of equivalences rather than isomorphisms.
For example, let 𝒫𝒶𝓇 be the supercategory of 𝒮e𝓉 with morphisms being ‘partial
functions’ (A ⟶ B) = (A → B + 𝟙)
where the extra element of 𝟙 = { * }
represents
‘undefined’ —also known as the Partial
, Option
, or Maybe
monads. Moreover,
let 𝒫𝒮ℯ𝓉 be the category of sets with an elected point. Then, 𝒫𝒶𝓇 ≃ 𝒫𝒮e𝓉
is
witnessed by (A ⟶ B) ↦ ( (A + 𝟙, *) ⟶ (B + 𝟙, *) )
and conversely
( (A , a) ⟶ (B , b) ) ↦ ( A  a ⟶ B  b)
where
X  x ≡ Σ y ∶ X • ¬(x ≡
y)
. Exercise: Work out the remaining details for the equivalence.
𝒮ℯ𝓉
tingsLet us give some elementary examples of the notion of a category to exhibit its ubiquity.
The collection of small, say level i
, types and functions between them and usual function composition
with usual identity form a category and this is not at all difficult to see:
instance 𝒮e𝓉 : ∀ {i} → Category {ℓsuc i} {i}  this is a ‘big’ category 𝒮e𝓉 {i} = record { Obj = Set i ; _⟶_ = λ A B → (A → B) ; _⨾_ = λ f g → (λ x → g (f x)) ; assoc = ≡refl ; Id = λ x → x ; leftId = ≡refl ; rightId = ≡refl }
Sadly, this category is traditionally used to motivate constructions in arbitrary categories and as such people usually think of objects in an arbitrary category as nothing more than sets with extra datum —which is completely false.
For example, the category Div
consists of objects and arrows both being numbers ℕ
and there is an arrow \(k : m → n\) precisely when k × m = n
, so that an arrow is a
constructive witness that \(m\) divides \(n\). Notice that besides ℕ, no sets nor functions
were involved in the making of this useful numbertheoretic category.
Recall that a type, or set, is nothing more than a specified collection of values.
Every set is also a category: There is a formal syntactic object associated with each element, the only morphisms are (formal)
identities, and composition is constantly a syntactic identity.
Some define a set to be a category with only identity morphisms; also called a
‘discrete category’ when one wants to distance themself from set theory ;)
—less loosely, a discrete category over a type S
has Obj = S
and (x ⟶ y) = (x ≡ y)
,
where the equivalence is classical, having two possible members true or false.
Discrete categories are quite an important space for hott people … that’s right, attractive people are interested in these things.
Observe that all arrows are invertible! —due to the symmetry of equality. Categories with this property are known as groupoids.
Recall that a monoid (M, ⊕, e)
is a type M
with an associative operation ⊕ : M × M → M
that has a unit e
.
Every monoid is also a category: There is one object, call it ★
, the morphisms are the monoid
elements, and composition is the monoid operation.
—less loosely, for a monoid (M, ⊕, e)
we take Obj = {★} , _⟶_ = M
.
In fact, some would define a monoid to be a oneobject category! –I'm looking at you Freyd & Scedrov =)
Recall that a preordered set, or preset, is a type P
with a relation ≤
on
it that satisfies indirect inequality from above:
\[
∀ x , y •\qquad x ≤ y \quad⇔\quad (∀ z •\; y ≤ z ⇒ x ≤ z)
\]
Equivalently, if it satisfies indirect equality from below:
\[ ∀ x , y •\qquad x ≤ y \quad⇔\quad (∀ z •\; z ≤ x ⇒ z ≤ y) \]
If we also have \(∀ x , y •\; x ≤ y \,∧\, y ≤ x \;⇒\; x = y\),
then we say (P, ≤)
is a ‘poset’ or an ‘ordered set’.
Every (pre)ordered set is also a category:
The objects are the elements,
the morphisms are the orderrelations,
identities are the relfexitivity of ≤
,
and composition is transitivity of ≤
.
To see this more clearly, suppose you have a group
\((M, ⊕, \_{}⁻¹, e)\) and you define \(x ≤ y \;⇔\; (∃ m : M •\; m ⊕ x = y)\)
then the this is a preorder whose induced category has a morphism
\(m : x → y\) precicely when \(m ⊕ x = y\)
–now sit down and define the remaining categorical structure of this
‘constructive’ preorder associated to the group.
Traditionally, classically, the relation ≤
is precicely a function P × P ⟶ 𝔹 = {true, flase}
and thus there is atmost one morphism between any two objects
–consequently, categories with this property are called poset categories.
In the constructive setting, the relation ≤
is typed P × P → Set
and then
for a preset (P, ≤)
we take Obj = P, _⟶_ = a ≤ b
and insist
on ‘proofirrelevance’ ∀ {a b} (p q : a ≤ b) → p ≡ q
so that there is at most one morphism
between any two objects.
The restriction is not needed if we were using actual categorieswithsetoids since then we would
define morphism equality to be
((a, b, p) ≈ (a’, b’, q) ) = (a ≡ a’ × b ≡ b’)
.
Observe that in the case we have a poset, every isomorphism is an equality: \[ ∀ x, y •\qquad x ≅ y ⇔ x ≡ y \] Categories with this property are called skeletal. Again, hott people like this; so much so, that they want it, moreorless, to be a foundational axiom!
Poset categories are a wonderful and natural motivator for many constructions and definitions in category theory. This idea is so broadreaching that it would not be an exaggeration to think of categories as coherently constructive lattices!
Equivalence relations are relations that are symmetric, reflexive, and transitive. Alternatively, they are preorder categories where every morphism is invertible —this is the symmetry property. But categories whose morphisms are invertible are groupoids!
Hence, groupoids can be thought of as generalized equivalence relations. Better yet, as “constructive” equivalence relations: there might be more than one morphism/construction witnessing the equivalence of two items.
Some insist that a true ‘set’ is a type endowed with an equivalence relation, that is a setoid. However, since groupoids generalise equivalence relations, others might insist on a true set to be a "groupoid". However, in the constructive setting of dependenttype theory, these notions coincide!
It’s been said that the aforementioned categories should be consulted whenever one learns a new concept of category theory. Indeed, these examples show that a category is a generalisation of a system of processes, a system of compositionality, and an ordered system.
Now the notion of structurepreserving maps, for categories, is just that of graphs but with attention to the algebraic portions as well.
record Functor {i j k l} (𝒞 : Category {i} {j}) (𝒟 : Category {k} {l}) : Set (ℓsuc (i ⊍ j ⊍ k ⊍ l)) where private instance 𝒞′ : Category ; 𝒞′ = 𝒞 𝒟′ : Category ; 𝒟′ = 𝒟 field  Usual graph homomorphism structure: An object map, with morphism preservation obj : Obj 𝒞 → Obj 𝒟 mor : ∀{x y : Obj 𝒞} → x ⟶ y → obj x ⟶ obj y  Interaction with new algebraic structure: Preservation of identities & composition id : ∀{x : Obj 𝒞} → mor (Id {A = x}) ≡ Id  identities preservation comp : ∀{x y z} {f : x ⟶ y} {g : y ⟶ z} → mor (f ⨾ g) ≡ mor f ⨾ mor g  Aliases for readability functor_preservescomposition = comp functor_preservesidentities = id open Functor public hiding (id ; comp)
For a functor F
, it is common practice to denote both obj F
and mor F
by F
and this is usually
not an issue since we can use type inference to deduce which is meant. However, in the Agda formalization
we will continue to use the names mor , obj
. Incidentally in the Haskell community, mor
is known as fmap
but we shall avoid that name or risk asymmetry in the definition of
a functor, as is the case in Haskell which turns out to be pragmatically useful.
A functor can be thought of as endowing an object with some form of structure
—since categories are intrinsically structureless in category theory—
and so the morphism component of a functor can be thought of as preserving relations:
f : a ⟶ b ⇒ F f : F a ⟶ F b
can be read as, ‘‘if a
is related to b
(as witnessed by f
)
then their structured images are also related (as witness by F f
)’’.
Recall the category Div
for constructive divisibility relationships ;)
A functor among monoids –construed as categories– is just a monoid homomorphism; i.e., an identity and multiplication preserving function of the carriers.
(M, ⊕, e) ⟶ (N, ⊗, d)  
=  Σ h ∶ M → N • ∀ x,y • h(x ⊕ y) = h x ⊗ h y ∧ h e = d 
By induction, h
preserves all finite ⊕multiplication and, more generally,
functors preserve finite compositions:
\[ F (f₀ ⨾ f₁ ⨾ ⋯ ⨾ fₙ) \;\;=\;\; F\,f₀ ⨾ F\,f₁ ⨾ ⋯ ⨾ F\,fₙ \]
Cool beans :)
In the same way, sit down and check your understanding!
Two examples of functors from a poset (category) to a monoid (category):
monus : (ℕ, ≤) ⟶ (ℕ,+, 0)
is a functor defined on morphisms by
\[ i ≤ j \quad⇒\quad \mathsf{monus}(i,j) ≔ j  i\]
Then the functor laws become i  i = 0
and (k  j) + (j  i) = k  i
.div : (ℕ⁺, ≤) → (ℚ, ×, 1)
is defined on morphisms by
\[i ≤ j \quad⇒\quad \mathsf{div}(i,j) ≔ j / i\]
The functor laws become i / i = 1
and (k / j) × (j / i) = k / i
.Hey, these two seem alarmingly similar! What gives! Well, they’re both functors from posets to monoids ;) Also, they are instances of ‘residuated pomonoids’. Noncommutative monoids may have not have a general inverse operation, but instead might have left and right inverse operations known as residuals —we’ll mention this word again when discussing adjunctions and are categorically seen as kan extensions. Alternatively, they’re are instances of ‘(Kopka) Differenceposets’.
For more examples of categories, we will need to reason by extensionality –two things are ‘equal’ when they have equivalent properties … recall Leibniz and his law of indiscernibles ;p
Categories have objects and morphisms between them, functors are morphisms between categories, and then we can go up another level and consider morphisms between functors. These ‘level 2 morphisms’ are pretty cool, so let’s touch on them briefly.
Recall that a poset ordering is extended to (possibly nonmonotone) functions \(f , g\) pointwise \[f \overset{.}{≤} g \quad\iff\quad (∀ x •\; f\, x \,≤\, g\, x)\] As such, with posets as our guide, we extend the notion of ‘morphism between functors’ to be a ‘witness’ of these orderings \(η : ∀ {X} → F\, X ⟶ G\, X\) –this is a dependent type, note that the second arrow notates category morphisms, whereas the first acts as a separator from the implicit parameter \(X\); sometimes one sees \(η_X\) for each component/instance of such an operation.
\(\require{AMScd}\)
\begin{CD} \color{navy}{F\, A} @>\color{fuchsia}{η_A}>> \color{teal}{G\, A} \\ @V\color{navy}{F\, f}VV \!= @VV\color{teal}{G\, f}V \\ \color{navy}{F\, B} @>>\color{fuchsia}{η_B}> \color{teal}{G\, B} \end{CD}
However, then for any morphism \(f : A ⟶ B\) we have two ways to get from \(F\, A\) to \(G\, B\) via
F f ⨾ η {B}
and η {A} ⨾ G f
and rather than choose one or the other, we request that they
are identical —similar to the case of associativity.
NatTrans : ∀ {i j i’ j’} ⦃ 𝒞 : Category {i} {j} ⦄ ⦃ 𝒟 : Category {i’} {j’} ⦄ (F G : Functor 𝒞 𝒟) → Set (j’ ⊍ i ⊍ j) NatTrans ⦃ 𝒞 = 𝒞 ⦄ ⦃ 𝒟 ⦄ F G = Σ η ∶ (∀ {X : Obj 𝒞} → (obj F X) ⟶ (obj G X)) • (∀ {A B} {f : A ⟶ B} → mor F f ⨾ η {B} ≡ η {A} ⨾ mor G f)
The naturality condition is remembered by placing the target component η {B}
after
lifting f
using the source functor F
;
likewise placing the source component before applying the target functor.
Another way to remember it:
η : F ⟶̇ G
starts at F
and ends at G
, so the naturality also starts with F
and ends
with G
; i.e., F f ⨾ η {B} = η {A} ⨾ G f
:)
It is at this junction that aforementioned problem with our definition of category comes to light: Function equality is extensional by definition and as such we cannot prove it. Right now we have two functionlike structures for which we will postulate a form of extensionality,
 function extensionality postulate extensionality : ∀ {i j} {A : Set i} {B : A → Set j} {f g : (a : A) → B a} → (∀ {a} → f a ≡ g a) → f ≡ g  functor extensionality postulate funcext : ∀ {i j k l} ⦃ 𝒞 : Category {i} {j} ⦄ ⦃ 𝒟 : Category {k} {l} ⦄ {F G : Functor 𝒞 𝒟} → (oeq : ∀ {o} → obj F o ≡ obj G o) → (∀ {X Y} {f : X ⟶ Y} → mor G f ≡ ≡subst₂ (Category._⟶_ 𝒟) oeq oeq (mor F f)) → F ≡ G  graph map extensionality postulate graphmapext : {G H : Graph } {f g : GraphMap G H} → (veq : ∀ {v} → ver f v ≡ ver g v) → (∀ {x y} {e : Graph._⟶_ G x y} → edge g e ≡ ≡subst₂ (Graph._⟶_ H) veq veq (edge f e)) → f ≡ g  natural transformation extensionality postulate nattransext : ∀ {i j i’ j’} {𝒞 : Category {i} {j} } {𝒟 : Category {i’} {j’} } {F G : Functor 𝒞 𝒟} (η γ : NatTrans F G) → (∀ {X} → proj₁ η {X} ≡ proj₁ γ {X}) → η ≡ γ
Natural transformations are too cool to end discussing so briefly and so we go on to discuss their usage is mathematics later on.
𝒞𝒶𝓉
With the notions of categories, functors, and extensionality inhand we can now discus the notion of the category of small categories and the category of small graphs. Afterwards we give another example of a functor that says how every category can be construed as a graph.
First the category of smaller categories,
𝒞𝒶𝓉
is a category of kind(ℓsuc m, ℓsuc m)
, wherem = i ⊍ j
, and its objects are categories of kind(i , j)
and so it is not an object of itself.Thankyou Russel and friends!
( You may proceed to snicker at the paradoxical and size issues encountered by those who use set theory. —Then again, I’ve never actually learned, nor even attempted to learn, any ‘‘formal set theory’’; what I do know of set theory is usually couched in the language of type theory; I heart LADM! )
instance 𝒞𝒶𝓉 : ∀ {i j} → Category {ℓsuc (i ⊍ j)} {ℓsuc (i ⊍ j)} 𝒞𝒶𝓉 {i} {j} = record { Obj = Category {i} {j} ; _⟶_ = Functor ; _⨾_ = λ {𝒞} {𝒟} {ℰ} F G → let instance 𝒞′ : Category ; 𝒞′ = 𝒞 𝒟′ : Category ; 𝒟′ = 𝒟 ℰ′ : Category ; ℰ′ = ℰ in record { obj = obj F ⨾ obj G  this compositon lives in 𝒮e𝓉 ; mor = mor F ⨾ mor G ; id = λ {x} → begin (mor F ⨾ mor G) (Id ⦃ 𝒞 ⦄ {A = x}) ≡⟨ "definition of function composition" ⟩′ mor G (mor F Id) ≡⟨ functor F preservesidentities evenunder (mor G) ⟩ mor G Id ≡⟨ functor G preservesidentities ⟩ Id ∎ ; comp = λ {x y z f g} → begin (mor F ⨾ mor G) (f ⨾ g) ≡⟨ "definition of function composition" ⟩′ mor G (mor F (f ⨾ g)) ≡⟨ functor F preservescomposition evenunder mor G ⟩ mor G (mor F f ⨾ mor F g) ≡⟨ functor G preservescomposition ⟩ (mor F ⨾ mor G) f ⨾ (mor F ⨾ mor G) g ∎ } ; assoc = λ {a b c d f g h} → funcext ≡refl ≡refl ; Id = record { obj = Id ; mor = Id ; id = ≡refl ; comp = ≡refl } ; leftId = funcext ≡refl ≡refl ; rightId = funcext ≡refl ≡refl }
Some things to note,
functor F preservescomposition evenunder mor G
is a real line of code!
It consists of actual function calls and is merely an alias for
≡cong (mor G) (mor F)
but it sure is far more readable than this form!
We could have written id = ≡cong (mor G) (id F) ⟨≡≡⟩ id G
,
but this is not terribly clear what is going on.
Especially since we introduced categories not too long ago,
we choose to elaborate the detail.
Likewise, comp = (≡cong (mor G) (comp F)) ⟨≡≡⟩ (comp G)
.
assoc
is trivial since function composition is, by definition, associative.
Likewise leftId, rightId
hold since functional identity is, by definition, unit of function composition.𝒢𝓇𝒶𝓅𝒽
In a nearly identical way, just ignoring the algebraic datum, we can show that
Graph
's with GraphMap
's form a graph
𝒢𝓇𝒶𝓅𝒽 : Category 𝒢𝓇𝒶𝓅𝒽 = {! exercise !}
𝒞𝒶𝓉
's are 𝒢𝓇𝒶𝓅𝒽
'sForgive and forget: The 𝒰nderlying functor.
Let’s formalise what we meant earlier when we said graphs are categories but ignoring the algebraic data.
Given a category, we ignore the algebraic structure to obtain a graph,
𝒰₀ : Category → Graph 𝒰₀ 𝒞 = record { V = Obj 𝒞 ; _⟶_ = Category._⟶_ 𝒞 }
Likewise, given a functor we ‘forget’ the property that the map of morphisms needs to preserve all finite compositions to obtain a graph map:
𝒰₁ : {𝒞 𝒟 : Category} → 𝒞 ⟶ 𝒟 → 𝒰₀ 𝒞 ⟶ 𝒰₀ 𝒟 𝒰₁ F = record { ver = obj F ; edge = mor F }
This says that 𝒰₁
turns ver, edge
into obj , mor
𝒰₁ ⨾ ver ≡ obj
and 𝒰₁ ⨾ edge ≡ mor
– reassuring us that 𝒰₁
acts
as a bridge between the graph structures: ver , edge
of graphs and
obj , mor
of categories.
Putting this together, we obtain a functor.
 Underlying/forgetful functor: Every category is a graph 𝒰 : Functor 𝒞𝒶𝓉 𝒢𝓇𝒶𝓅𝒽 𝒰 = record { obj = 𝒰₀ ; mor = 𝒰₁ ; id = ≡refl ; comp = ≡refl }
We forget about the extra algebraic structure of a category and of a functor to
arrive at a graph and graphmap, clearly ≡refl
– such ‘forgetfullness’ preserves identities
and composition since it does not affect them at all!
Those familiar with category theory may exclaim that just as I have mentioned the names ‘underlying functor’ and ‘forgetful functor’ I ought to mention ‘stripping functor’ as it is just as valid since it brings about connotations of ‘stripping away’ extra structure. I’m assuming the latter is less popular due to its usage for poor mathematical jokes and puns.
Before we move on, the curious might wonder if ‘‘categories are graphs’’ then what is the analgoue to ‘‘\(X\) are hypergraphs’’, it is multicategories.
Now the remainder of these notes is to buildup the material needed to realise the notion of ‘free’ which is, in some sense, the bestpossible approximate inverse to ‘forgetful’ –however, forgetting is clearly not invertible since it can easily confuse two categories as the same graph!
Recall, that a natural transformation \(η : F \natTo G\) is a family \(∀ \{X \,:\, \Obj 𝒞\} \,→\, F\, X ⟶ G\, X\) that satisfies the naturality condition: \(∀ \{A \; B\} \{f \,:\, A ⟶ B\} \,→\, F f ⨾ η {B} \;≡\; η {A} ⨾ G f\).
∀{X : Obj 𝒞} → F X → G → X
and invocation η {X}
.\(\require{AMScd}\)
\begin{CD} \color{navy}{F\, A} @>\color{fuchsia}{η_A}>> \color{teal}{G\, A} \\ @V\color{navy}{F\, f}VV \!= @VV\color{teal}{G\, f}V \\ \color{navy}{F\, B} @>>\color{fuchsia}{η_B}> \color{teal}{G\, B} \end{CD}Let us look at this from a few different angles; in particular, what does the adjective ‘natural’ actually mean? It’s been discussed on many forums and we collect a few of the key points here.
Given two functors \(F , G\), for any object \(~x\) we obtain two objects \(F\, x\, , \, G\, x\) and so a morphism
from \(F\) to \(G\) ought to map such \(F\,x\) to \(G\, x\). That is, a morphsim of functors is a family
\(η \,:\, ∀ \{x : \Obj 𝒞\} \,→\, F \,x ⟶ G \,x\). Now for any \(f : a → b\) there are two ways to form a morphism
\(F\, a → G\, b\): \(F f ⨾ η \{b\}\) and \(η \{a\} ⨾ G\, f\). Rather than make a choice each time we want such
a morphism, we eliminate the choice all together by insisting that they are identical.
This is the naturality condition.
This is similar to when we are given three morphisms \(f : a → b , g : b → c , h : c → d\), then there are two ways to form a morphism \(a → d\); namely \((f ⨾ g) ⨾ h\) and \(f ⨾ (g ⨾ h)\). Rather than make a choice each time we want such a morphism, we eliminate the choice all together by insisting that they are identical. This is the associativity condition for categories.
Notice that if there’s no morphism \(F\, x ⟶ G\, x\) for some \(x\), they by definition there’s no possible natural transformation \(F \natTo G\).
That is,
\begin{align*} & \quad \text{it is a natural construction/choice} \\ = & \quad \text{distinct people would arrive at the same construction;} \\ & \quad \text{ (no arbitrary choice or cleverness needed) } \\ = & \quad \text{ there is actually no choice, i.e., only one possiility, } \\ & \quad \text{ and so two people are expected to arrive at the same ‘choice’} \end{align*}Thus, if a construction every involves having to decide between distinct routes, then chances are the result is not formally natural. Sometimes this ‘inution’ is developed from working in a field for some time; sometimes it just “feel”" natural.
Some would even say: Natural = Godgiven.
A natural transformation can be thought of as a polymorphic function
∀ {X} → F X ⟶ G X
where we restrict ourselves to avoid inspecting any X
.
mono
morphic operation makes no use of type variables in its signature,
whereas a poly
morphic operation uses type variables in its signature.is
a subclass of another thereby
obtaining specific information, whereas there is no such mechanism in Haskell.Inspecting type parameters or not leads to the distinction of ad hoc plymorphism vs. parametric polymorphism —the later is the kind of polymorphism employed in functional language like Haskell and friends and so such functions are natural transformations by default! Theorems for free!
For example,
 Let 𝒦 x y ≔ Id {x} for morphisms, and 𝒦 x y ≔ x for objects. size : ∀ {X} → List X → 𝒦 ℕ X size [x₁, …, xₙ] = n
is a polymorphic function and so naturality follows and is easily shown –show it dear reader!
So we have always have
\[List\; f \;⨾\; size \quad=\quad size\]
Since 𝒦 ℕ f = Id
, then by extensionality: size : List ⟶̇ 𝒦
.
On the other hand, the polymorphic function
whyme : ∀ {X} → List X → 𝒦 Int X whyme {X} [x₁,…,xₙ] = If X = ℕ then 1729 else n
is not natural: The needed equation F f ⨾ η {B} = η {A} ⨾ G f
for any f : A → B
breaks as witnessed by
f = (λ x → 0) : ℝ → ℕ
and any list with length n ≠ 1729
,
and this is easily shown –do so!
One might exclaim, hey! this only works ’cuz you’re using Ramanujan’s taxicab number! 1729 is the smallest number expressible as a sum of 2 cubes in 2 ways: \(1729 = 12³ + 1³ = 10³ + 9 ³\). I assure you that this is not the reason that naturality breaks, and I commend you on your keen observation.
Notice that it is natural if we exclude the type inspected, ℕ. That is, if we only consider \(f : A → B\) with \(A ≠ ℕ ≠ B\). In general, is it the case that a transformation can be made natural by excluding the types that were inspected?
Before we move on, observe that a solution in \(h\) to the absorptiveequation \(F f ⨾ h = h\) is precisely a natural transformation from \(F\) to the aforementioned ‘diagonal functor’: \[F f ⨾ h \;=\; h \qquad⇔\qquad ∃ X : Obj \;•\; h ∈ F \overset{.}{⟶} 𝒦 X ~\]
In particular, due to the constantfusion property \(g \,⨾\, 𝒦\, e \;=\; 𝒦\, e\), we have that \[∀ \{F\} \{X\} \{e \,:\, X\} \;→\; (𝒦\, e) \,∈\, F \overset{.}{⟶} 𝒦\, X \] Is the converse also true? If \(h ∈ F ⟶̇ 𝒦 X\) then \(h \,=\, 𝒦\, e\) for some \(e\)?
The idea that a natural transformation cannot make reference to the type variable at all can be seen by yet another example.
data 𝟙 : Set where ★ : 𝟙  Choice function: For any type X, it yields an argument of that type. postulate ε : (X : Set) → X nay : ∀ {X} → X → X nay {X} _ = ε X
Now naturality \(\Id \, f ⨾ nay_B \;=\; nay_A ⨾ \Id \, f\) breaks as witnessed by \(f \;=\; (λ _ → εℕ + 1) \;:\; 𝟙 → ℕ\) –and provided \(εℕ ≠ 0\), otherwise we could use an \(f\) with no fix points.
From this we may hazard the following: If we have natural transformations \(ηᵢ \,:\, ∀ {X : Objᵢ} →\, F X \overset{.}{⟶} G X\) where the \(Objᵢ\) partition the objects available — i.e., \(Obj \;=\; Σ i \,•\, Objᵢ\) — then the transformation \(η_{(i, X)} \;=\; ηᵢ\) is generally unnatural since it clearly makes choices, for each partition.
A family of morphisms is natural in x precisely when it is defined simultaneously for all x —there is no inspection of some particular x here and there, no, it is uniform! With this view, the naturality condition is thought of as a ‘simultaneity’ condition. Rephrasing GToNE.
The idea of naturality as uniformlydefinable is pursued by Hodges and Shelah.
Recall that a functor can be thought of as endowing an object with structure. Then a transformation can be thought of as a restructuring operation and naturality means that it doesn’t matter whether we restructure or modify first, as long as we do both.
It may help to think of there’s a natural transformation from F to G to mean there’s an obvious/standard/canconical way to transform F structure into G structure.
Likewise, F is naturally isomorphic to G may be read F is obviously isomorphic to G. For example, TODO seqpair or pairseq ;)
Sometimes we can show ‘‘F X is isomorphic to G X, if we make a choice dependent on X’’ and so the isomorphism is not obvious, since a choice must be made.
F f ⨾ η {B} = η {A} ⨾ G f
from left to right:
Mapping \(f\) over a complicated structure then handling the result
is the same as
handling the complex datum then mapping \(f\) over the result.
Lists give many examples of natural transformations by considering a categorical approach to the theory of lists.
The naturality condition can be seen as a rewrite rule that let’s us replace a complicated or inefficient side with a simplier or more efficient yet equivalent expression. I think I first learned this view of equations at the insistence of Richard Bird and Oege de Moor –whose text can be found here, albeit the legitimacy of the link may be suspect.
For example, recall the 𝒦onstant functor now construed only as a polymorphic binary operation:
_⟪_ : ∀{A B : Set} → A → B → A a ⟪ b = a
The above is a constant time operation, whereas the next two are linear time operations; i.e.,
they take n
steps to compute, where n
is the length of the given list.
 This' map for List's; i.e., the mor of the List functor map : ∀ {A B : Set} (f : A → B) → List A → List B map f [] = [] map f (x ∷ xs) = f x ∷ map f xs  Interpret syntax `x₀∷⋯∷xₙ₋₁` semantically as `x₀ ⊕ ⋯ ⊕ xₙ₋₁`, where ⊕ = cons. fold : ∀ {A B : Set} (cons : A → B → B) (nil : B) → List A → B fold cons nil [] = nil fold cons nil (x ∷ xs) = cons x (fold cons nil xs)
By Theorems for Free, or a simple proof, we have that fold
is a natural
transformation \(List \overset{.}{→} \Id\):
\[ List\; f \;⨾\; fold \; cons_B \; nil_B \qquad=\qquad fold \; cons_A \; nil_A \;⨾\; f \]
Note that here we are ranging over objects \(X\) equipped with \(nil_X : X, \; cons_X : X → X → X\);
as such the equation is not valid when this is not the case.
Now we map compute,
postulate A B : Set postulate nilB : B postulate f : A → B  possibly expensive operation head : List B → B head = fold _⟪_ nilB compute : List A → B compute = map f ⨾ head
That is,
compute [x₀, …, xₙ₋₁] = head (map f [x₀, …, xₙ₋₁]) = head [f x₀, …, f xₙ₋₁] = f x₀ ⟪ f x₁ ⟪ ⋯ ⟪ ⋯ f xₙ₋₁ ⟪ nilB = f x₀
However this approach performs the potentially expensive operation \(f\) a total of
\(n = \text{“length of input”}\) times! In spite of that, it only needs the first element of
the list and performs the operation only once! Indeed, by the naturality of fold
we have
an equivalent, and more efficient, formulation:
compute = head ⨾ f
This operation only performs the potentially costly f
once!
A more concrete and realistic example is to produce an efficient version of the function
that produces the average xs = div (sum xs, length xs)
of a list of numbers: This operation
traverses the input list twice, yet we can keep track of the length as we sumalong the list
to obtain an implementation that traverses the list only once!
div : ℕ × ℕ → ℕ div (0, 0) = 0 div (m, n) = m ÷ n average : List ℕ → ℕ average xs = div (fold _⊕_ 𝟘 xs) where 𝟘 = (0 , 0) _⊕_ : ℕ → (ℕ × ℕ) → ℕ a ⊕ (b , n) = (a + b , n + 1)
Given two functors \(F , G : 𝒞 ⟶ 𝒟\) let us construe them as only graph homomorphisms.
Then each is a model of the graph \(𝒰₀ \; 𝒞\) —each intereprets the nodes and edges of 𝒰₀ 𝒞
as
actual objects and morphisms of 𝒟— and a natrual transformation is then nothing
more than a morphism of models.
In the setting of types and functions, η : F ⟶̇ G
means we have η (F f x) = G f (η x)
which when read lefttoright says that η
is defined by patternmatching on its argument
to obtain something of the form F f x
then it is defined recursively by examining x
and then
applying G f
to the result —of course there’s some base case F
definitions as well.
Alternatively, the input to η
is of the form F …
and its
output is of the form G …
–mind blown!
For example, I want to define a transformation \(\mathsf{List} ⟶̇ \mathsf{List}\).
[f x₀, f x₁, …, f xₙ₋₁]
–for arbitrary \(f : A → B\).[f y₀, f y₁, …, f yₘ₋₁]
where \(y \,=\, η\,x\).So my only choices are \(y : \List A\) and \(m : ℕ\)
Here are some possibilities and the resulting η:
[]
functionA functor among monoids –construed as categories– is just a monoid homomorphism:
\begin{align*} & (M, ⊕, e) ⟶ (N, ⊗, d) {{{newline}}} ≅ \quad & Σ h ∶ M → N • ∀ \{x \, y \} •\; h(x ⊕ y) = h x ⊗ h y \lands h e = d \end{align*}
A natural transformation (f, prf) ⟶ (g, prf’)
is a point \(n : N\) with
\(∀ x ∶ M \;•\; f x ⊗ n \,=\, n ⊗ g x\), a socalled ‘conjugation’ by \(n\) that takes \(f\) to \(g\).
Recall from the introduction \(𝒰(S, ⊕, e) \;=\; S\) was the underlying functor from monoids to sets.
Let \(𝒰 × 𝒰\) be the functor that for objects \(M \;↦\; 𝒰\, M \,×\, 𝒰\, M\) and for morphisms \(h \;↦\; λ (x,y) → (h\, x, h\, y)\). Then the monoid multiplication (of each monoid) is a natural transformation \(𝒰 × 𝒰 \natTo 𝒰\), where naturality says that for any monoid homomorphism \(h\), the application of \(𝒰\, h\) to the (monoid) multiplication of two elements is the same as the (monoid) multiplication of the \(𝒰\, h\) images of the two elements, and this is evident from the homomorphism condition.
Extending to finite products, \(ℒ \;≔\; (Σ n ∶ ℕ • ∏ i ∶ 1..n • 𝒰)\), the natural transformation
\(ℒ \natTo 𝒰\) is usually called fold, reduce, or cata and ℒ
is known as the
free monoid functor with notations \(A* \;=\; \List A \;=\; ℒ\, A\).
Loosely put,
ℒ₀ : Monoid → Set ℒ₀ M = Σ n ∶ ℕ • ∏ i : 1..n • 𝒰 M  finite sequences of elements from M ℒ₁ : ∀ {M N : Monoid} → (M ⟶ N) → ℒ₀ M → ℒ₀ N ℒ₁ (h , prf) = λ (n , x₁, …, xₙ) → (n , h x₁ , … , h xₙ) fold : ∀ {M : Monoid} → ℒ₀ M → 𝒰₀ M fold {(M, ⊕, e)} = λ (n , x₁, …, xₙ) → x₁ ⊕ ⋯ ⊕ xₙ
–The reader would pause to consider implementing this formally using Agda's Data.Fin
and Data.Vec
;)–
Now for any monoid homomorphism h
, applying induction, yields
h₀(x₁ ⊕ ⋯ ⊕ xₙ) = h₀ x₁ ⊕ ⋯ ⊕ h₀ xₙ where h₀ = 𝒰 (h₀, prf) = 𝒰 h
Which is easily seen to be just naturality – if we use backwards composition \(f ⨾ g \;=\; g ∘ f\) –
𝒰 h ∘ fold {M} = fold {N} ∘ ℒ h
Woah!
This is mentioned in the Barr and Wells' Category Theory for Computing Science text, citing Linton, 1969ab.
For example, src, tgt
—from the graph signature— give natural transformations
\(V \natTo E\) from the vertex functor to the edge functor … keep reading ;)
Recall that \(V(G)\) is essentially \(ℙ₀ ⟶ G\) where
\(ℙₙ\) is the graph of \(n\) edges on \(n+1\) vertices named \(0..n\) with typing \(i \,:\, i1 ⟶ i\),
which I like to call the path graph of length n; and in particular \(ℙ₀\) is the graph of
just one dot, called 0, and no edges. —Earlier I used the notation [n]
, but I’m using \(ℙ\) since
I like the view point of ℙaths.
What does it mean to say that V(G) is essentially ℙ₀ ⟶ G?
It means that the vertices functor
– \(𝒱 \;:\; 𝒢𝓇𝒶𝓅𝒽 ⟶ 𝒮ℯ𝓉\) that takes objects \(G ↦ V(G)\) and morphisms \(h ↦ \mathsf{ver}\, h\) –
can be ‘represented’ as the Hom functor \((ℙ₀ ⟶ \_{})\), that is to say
\[𝒱 \quad≅\quad (ℙ₀ ⟶ \_{}) \;\mathsf{within \; Func} \; 𝒢𝓇𝒶𝓅𝒽 \; 𝒮ℯ𝓉\]
Func
tor categories will be defined in the next section!–
Notice that we arrived at this expression by ‘etareducing’ the phrase V(G) is essentially ℙ₀ ⟶ G! ;)
More generally, we have the functor \(ℙₙ ⟶ \_{}\) which yields all paths of length \(n\) for a given graph.
Observe –i.e., show– that we also have an edges functor.
With a notion of morphisms between functors, one is led inexorably to ask whether functors as objects and natural transformations as morphisms constitute a category? They do! However, we leave their definition to the reader —as usual, if the reader is ever so desperate for solutions, they can be found as comments in the unruliness that is the source file.
instance Func : ∀ {i j i’ j’} (𝒞 : Category {i} {j}) (𝒟 : Category {i’} {j’}) → Category _ Func 𝒞 𝒟 = {! exercise !}
This is a good exercise as it will show you that there is an identity functor and that composition of functors is again a functor. Consequently, functors are in abundance: Given any two, we can form [possibly] new ones by composition.
It is a common construction that when a type \(Y\) is endowed with some structure, then we can endow the function space \(X → Y\), where \(X\) is any type, with the same structure and we do so ‘pointwise’. This idea is formalised by functor categories. Alternatively, one can say we have ‘categorified’ the idea; where categorification is the process of replacing types and functions with categories and functors and possibly adding some coherence laws.
There are people who like to make a show about how ‘big’ 𝒞𝒶𝓉 or Func 𝒞 𝓓
are;
these people adhere to something called ‘set theory’ which is essentialy type theory but
ignoring types, loosely put they work only with the datatype
data SET : Set where Elem : ∀ {A : Set} → A → SET
Such heathens delegate typesoftypes into ‘classes’ of ‘small’ and ‘big’ sets and it’s not uniform enough for me. Anyhow, such people would say that functor categories ‘‘cannot be constructed (as sets)’’ unless one of the categories involved is ‘‘small’’. Such shenanigans is ignored due to the hierarchy of types we are using :)
We must admit that at times the usage of a single type, a ‘unityped theory’ if you will can be
used when one wants to relise types in an extrinsic fashion rather than think of data as
intrinsically typed –E.g., graphs with src, tgt
then deriving a notion of ‘type’ with _⟶_
.
Everything has its place … nonetheless, I prefer (multi)typed settings!
Let 𝟙 ≔ [ • ]
be the discrete category of one object (and only the identity arrow on it).
Then 𝒞 ≅ Func 𝟙 𝒞
.
Let 𝟚₀ ≔ [• •]
be the discrete category of two objects.
Then the 𝒞squared category can be defined 𝒞 ⊗ 𝒞 ∶≅ Func 𝟚₀ 𝒞
:
This category essentially consists of pairs of 𝒞objects with pairs of 𝒞arrows
between them.
The subscript 0 is commonly used for matters associated with objects and
the name 𝟚₀
is suggestive of the category of 2 objects only.
More generally, if 𝒩 is the discrete category of \(n\) objects, then
the nfold product category is defined by
(∏ i ∶ 1..n • 𝒞) ∶≅ Func 𝒩 𝒞
.
These are also commonly denoted \(𝒞^2\) and \(𝒞^𝒩\) since they are essentially
products, and more generally Func 𝒳 𝒴
is also denoted 𝒴^{𝒳} and referred.
We can add an arrow to 𝟚₀
to obtain another category…
Let 𝟚 ≔ • ⟶ •
be the category of two objects, call them 0 and 1, with one arrow between them.
Then a functor 𝟚 ⟶ 𝒞
is precisely a morphism of 𝒞 and a natural transformation
f ⟶ g
boils down to just a pair of morphisms (h,k)
with h ⨾ g = f ⨾ k
.
Hence, the arrow category of 𝒞 is \(𝒞^𝟚 \;≅\; 𝒞^→ \;≅\; \mathsf{Func}\, 𝟚 𝒞\); which is essentially the category with objects being 𝒞morphisms and morphisms being commutative squares.
Notice that a functor can be used to
Likewise, a natural transformation can be used to select a commutative diagram.
It is a common heuristic that when one suspects the possibility of a category 𝒞
, then one
can make probes to discover its structure. The objects are just functors 𝟙 ⟶ 𝒞
and the
morphisms are just functors 𝟚 ⟶ 𝒞
.
The category of presheaves of 𝒞 is the category PSh 𝒞 ≔ Func (𝒞 ᵒᵖ) 𝒮e𝓉
.
This is a pretty awesome category since it allows nearly all constructions in 𝒮ℯ𝓉 to be realised! Such as subsets, truth values, and even powersets! All these extra goodies make it a ‘topos’ aka ‘power allegory’ —the first is a category that has all finite limits and a notion of powerset while the second, besides the power part, looks like a totally different beast; the exhilaration!
The slice category of 𝒞 over B : Obj 𝒞 is the category 𝒞 / B ≔ Σ F ∶ Func 𝟚 𝒞 • (F 1 = B)
.
Essentially it is the category of objects being 𝒞morphisms with target \(B\) and morphisms \(f ⟶ g\) being \((h,k)\) with \(h ⨾ g = f ⨾ k\) but a natural choice for \(k : B ⟶ B\) is \(\Id_B\) and so we can use morphism type \((f ⟶’ g) \;≔\; Σ h : \src f ⟶ \src g \;•\; h ⨾ g = f\).
This is seen by the observation \[(h, k) \;∈\; f ⟶ g \qquad⇔\qquad h \;∈\; (f ⨾ k) ⟶’ g\] Of course a formal justification is obtained by showing \[\_{}⟶\_{} \quad≅\quad \_{}⟶’\_{} \quad \mathsf{within \; Func }\; (𝒞 ᵒᵖ ⊗ 𝒞) 𝒮e𝓉 \] …which I have not done and so may be spouting gibberish!
Just as the type Σ x ∶ X • P x
can be included in the type X
, by forgetting the second
component, so too the category Σ F ∶ 𝟚 ⟶ 𝒞 • F 1 ≈ B
can be included into the category
𝒞 and we say it is a subcategory of 𝒞.
The notation Σ o ∶ Obj 𝒞 • P o
defines the subcategory of 𝒞 obtained by deleting
all objects not satisfying predicate P
and deleting all morphisms incident to such objects; i.e.,
it is the category 𝒟 with
\[ \Obj 𝒟 \quad≡\quad Σ o ∶ \Obj 𝒞 \,•\, P o
\qquad\text{ and }\qquad
(o , prf) ⟶_𝒟 (o' , prf') \quad≡\quad o ⟶_𝒞 o'
\]
This is the largest/best/universal subcategory of 𝒞 whose objects satisfy \(P\).
Formalise this via a universal property ;)
𝒮e𝓉
are Functor Categories\[ \Func \; S \; 𝒮e𝓉 \qquad≅\qquad 𝒮e𝓉 / S \] Where S in the left is construed as a discrete category and in the right is construed as an object of 𝒮e𝓉.
This is because a functor from a discrete category need only be a function of objects since there are no nonidentity morphisms. That is, a functor \(f : S ⟶ 𝒮ℯ𝓉\) is determined by giving a set \(f\,s\) for each element \(s ∈ S\) —since there are no nonidentity morphisms. Indeed a functor \(f : S ⟶ Set\) yields an Stargeted function \[ (Σ s ∶ S \,•\, f\, s) → S \quad:\quad λ (s , fs) → s \] Conversely a function \(g : X → S\) yields a functor by sending elements to their preimage sets: \[ S ⟶ Set \quad:\quad λ s → (Σ x ∶ X \,•\, g\, x ≡ s)\]
Because of this example, 𝒞 / B
can be thought of as ‘𝒞objects indexed by B’
–extending this idea further leads to fibred categories.
In a similar spirit, we can identify natural transformations as functors! \[\Func \, 𝒞 \, (𝒟 ^ 𝟚) \quad≅\quad (Σ F , G ∶ 𝒞 ⟶ 𝒟 \;•\; \mathsf{NatTrans}\, F\, G)\]
A functor \(N : 𝒞 ⟶ 𝒟 ^ 𝟚\) gives, for each object \(C : \Obj 𝒞\) an object in \(𝒟 ^ 𝟚\) which is precisely an arrow in \(𝒟\), rewrite it as \(N_C : F\,C ⟶ G\,C\) where \(F\,C \,≔\, N\, C\, 0\) and \(G\, C \,≔\, N\, C\, 1\).
Likewise, for each arrow \(f : A ⟶ B\) in 𝒞 we obtain an arrow \(N\, f \,:\, N\, A ⟶ N\, B\) in \(𝒟 ^ 𝟚\) which is precisely a commutative square in 𝒟; that is, a pair of 𝒟arrows \((F\,f , G\,f) ≔ N\,f\) with \(N_A ⨾ G\,f \;=\; F\,f ⨾ N_B\).
Notice that we have implicitly defined two functors \(F, G : 𝒞 ⟶ 𝒟\). Their object and morphism mappings are clear, but what about functoriality? We prove it for both \(F, G\) together.
Identity:
\begin{calc} (F \,\Id \, , \, G\, \Id) \step{ definition of $F$ and $G$ } N \, \Id \step{ $N$ is a functor } \Id \,∶\, 𝒟 ^ 𝟚 \step{ identity in arrow categories } (\Id , \Id) \end{calc}Composition:
\begin{calc} ( F (f ⨾ g) , G (f ⨾ g) ) \step{ definition of $F$ and $G$ } N\, (f ⨾ g) \step{ $N$ is a functor } N\, f ⨾ N\, g \step{ definition of $F$ and $G$ } (F\, f, G\, f) ⨾ (F\,g , G\,g) \step{ composition in arrow categories } (F\,f ⨾ F\,g , G\,f ⨾ G\,g) \end{calc}Sweet!
Conversely, given a natural transformation \(η : F \overset{.}{⟶} G\) we define a functor \(N : 𝒞 ⟶ 𝒟 ^ 𝟚\) by sending objects \(C\) to \(η_C : F\, C ⟶ G\, C\), which is an object is \(𝒟 ^ 𝟚\), and sending morphisms \(f : A ⟶ B\) to pairs \((G f , F f)\), which is a morphism in \(𝒟 ^ 𝟚\) due to naturality of η; namely \(η_A ⨾ G\, f \;=\; F\, f ⨾ η_B\). It remains to show that \(N\) preserves identities and composition –Exercise!
Now it remains to show that these two processes are inverses and the isomorphism claim is complete. Woah!
Similarly, to show \[ \Func\, (𝟚 ⊗ 𝒞) \, 𝒟 \qquad≅\qquad (Σ F₀ , F₁ ∶ 𝒞 ⟶ 𝒟 • \mathsf{NatTrans}\, F₁ \, F₂)\]
By setting \(H\, i \;=\; Fᵢ\) on objects and likewise for morphisms
but with \(H(\Id, 1) \;=\; η\) where \(1 : 0 ⟶ 1\) is the nonidentity arrow of 𝟚
.
(Spoilers! Alternatively: Arr (Func 𝒞 𝒟) ≅ 𝟚 ⟶ 𝒞 ^ 𝒟 ≅ 𝒞 × 𝟚 ⟶ 𝒟
since 𝒞𝒶𝓉
has exponentials,
and so the objects are isomorphic; i.e., natural transformations correspond to functors 𝒞×𝟚⟶𝒟
)
Why are we mentioning this alternative statement? Trivia knowledge ofcourse!
On a less relevant note, if you’re familiar with the theory of stretchingwithouttearing, formally known as topology which is pretty awesome, then you might’ve heard of paths and deformations of paths are known as homotopies which are just continuous functions \(H : X × I ⟶ Y\) for topological spaces $X, Y,$ and \(I \,=\, [0,1]\) being the unit interval in ℝ. Letting \(𝒥 = 𝟚\) be the ‘categorical interval’ we have that functors \(𝒞 × 𝒥 ⟶ 𝒟\) are, by the triviarelevant result, the same as natural transformations. That is, natural transformations extend the notion of homotopies, or pathdeformations.
On mathoverflow, the above is recast succinctly as: A natural transformation from \(F\) to \(G\) is a functor, targeting an arrow category, whose ‘source’ is \(F\) and whose ‘target’ is \(G\). \[ \hspace{2em} F \overset{.}{⟶} G : 𝒞 ⟶ 𝒟 \quad≅\quad Σ η ∶ 𝒞 ⟶ \mathsf{Arr}\, 𝒟 •\; \mathsf{Src} ∘ η = F \;\;∧\;\; \mathsf{Tgt} ∘ η = G \] Where, the projection functors
\begin{align*} \mathsf{Src}& &:& \mathsf{Arr}\, 𝒟 ⟶ 𝒟 \\ \mathsf{Src}& (A₁ , A₂ , f) &=& A₁ \\ \mathsf{Src}& (f , g , h₁ , h₂ , proofs) &=& h₁ \end{align*}with \(\mathsf{Tgt}\) returning the other indexed items.
We give an example of a functor by building on our existing graphs setup. After showing that graphs correspond to certain functors, we then mention that the notion of graphmap is nothing more than the associated natural transformations!
module graphsasfunctors where
Let us construct our formal graph category, which contains the ingredients for
a graph and a category and nothing more than the equations needed of a category.
The main ingredients of a twosorted graph are two sortsymbols E, V
, along with
two functionsymbols s, t
from E
to V
—this is also called ‘the signature
of graphs’. To make this into a category, we need functionsymbols id
and a composition
for which it is a unit.
 formal objects data 𝒢₀ : Set where E V : 𝒢₀  formal arrows data 𝒢₁ : 𝒢₀ → 𝒢₀ → Set where s t : 𝒢₁ E V id : ∀ {o} → 𝒢₁ o o  (forward) composition fcmp : ∀ {a b c} → 𝒢₁ a b → 𝒢₁ b c → 𝒢₁ a c fcmp f id = f fcmp id f = f
Putting it all together,
instance 𝒢 : Category 𝒢 = record { Obj = 𝒢₀ ; _⟶_ = 𝒢₁ ; _⨾_ = fcmp ; assoc = λ {a b c d f g h} → fcmpassoc f g h ; Id = id ; leftId = leftid ; rightId = rightid } where  exercises: prove associativity, left and right unit laws
Now we can show that every graph G
gives rise to a functor: A semantics of 𝒢
in 𝒮e𝓉
.
toFunc : Graph → Functor 𝒢 𝒮e𝓉 toFunc G = record { obj = ⟦_⟧₀ ; mor = ⟦_⟧₁ ; id = ≡refl ; comp = λ {x y z f g} → fcmp⨾ {x} {y} {z} {f} {g} } where ⟦_⟧₀ : Obj 𝒢 → Obj 𝒮e𝓉 ⟦ 𝒢₀.V ⟧₀ = Graph.V G ⟦ 𝒢₀.E ⟧₀ = Σ x ∶ Graph.V G • Σ y ∶ Graph.V G • Graph._⟶_ G x y ⟦_⟧₁ : ∀ {x y : Obj 𝒢} → x ⟶ y → (⟦ x ⟧₀ → ⟦ y ⟧₀) ⟦ s ⟧₁ (src , tgt , edg) = src ⟦ t ⟧₁ (src , tgt , edg) = tgt ⟦ id ⟧₁ x = x  Exercise: fcmp is realised as functional composition fcmp⨾ : ∀{x y z} {f : 𝒢₁ x y} {g : 𝒢₁ y z} → ⟦ fcmp f g ⟧₁ ≡ ⟦ f ⟧₁ ⨾ ⟦ g ⟧₁
Conversely, every such functor gives a graph whose vertices and edges are the sets
associated with the sortsymbols V
and E
, respectively.
fromFunc : Functor 𝒢 𝒮e𝓉 → Graph fromFunc F = record { V = obj F 𝒢₀.V ; _⟶_ = λ x y → Σ e ∶ obj F 𝒢₀.E • src e ≡ x × tgt e ≡ y  the type of edges whose source is x and target is y } where tgt src : obj F 𝒢₀.E → obj F 𝒢₀.V src = mor F 𝒢₁.s tgt = mor F 𝒢₁.t
It is to be noted that we can define ‘‘graphs over 𝒞’’ to be the category Func 𝒢 𝒞
.
Some consequences are as follows: Notion of graph in any category, the notion of graphmap
is the specialisation of natural transformation (!), and most importantly, all the power of functor categories
is avaiable for the study of graphs.
In some circles, you may hear people saying an ‘algebra over the signature of graphs’ is an interpretation
domain (𝒞
) and an operation (Functor 𝒢 𝒞
) interpreting the symbols. Nice!
We briefly take a pause to look at the theory of category theory.
In particular, we show a pair of constructions to get new categories from old ones,
interpret these constructions from the view of previously mentioned categories, and
discuss how to define the morphism type _⟶_
on morphisms themselves, thereby
yielding a functor.
The ‘dual’ or ‘opposite’ category 𝒞ᵒᵖ is the category constructed from 𝒞 by reversing arrows: \((A ⟶_{𝒞ᵒᵖ} B) \;≔\; (B ⟶_𝒞 A)\), then necessarily \((f ⨾_{𝒞ᵒᵖ} g) \;≔\; g ⨾_𝒞 f\). A ‘contravariant functor’, or ‘cofunctor’, is a functor F from an opposite category and so there is a reversal of compositions: \(F(f \,⨾\, g) \;=\; F g \,⨾\, F f\).
_ᵒᵖ : ∀ {i j} → Category {i} {j} → Category 𝒞 ᵒᵖ = {! exercise !}
Notice that \((𝒞 ᵒᵖ) ᵒᵖ \;=\; 𝒞\) and \(𝒞 ᵒᵖ \;≅\; 𝒞\) –one may have an intuitive idea of what this isomorphsim means, but formally it is only meaningful in the context of an ambient category; keep reading ;)
We must admit that for categories, the notion of isomorphism is considered less useful than that of equivalence which weakens the condition of the tofrom functors being inverses to just being naturally isomorphic to identities; C.f., ‘evil’ above.
Some interpretations:
𝒮e𝓉ᵒᵖ is usual sets and functions but with ‘backwards composition’:
infix 10 _∘_ _∘_ : ∀ {i j } ⦃ 𝒞 : Category {i} {j}⦄ {A B C : Obj 𝒞} → B ⟶ C → A ⟶ B → A ⟶ C f ∘ g = g ⨾ f
Indeed, we have g ⨾ f within 𝒞 = f ∘ g within 𝒞 ᵒᵖ
; which is how these composition operators
are usually related in informal mathematics (without mention of the ambient categories of course).
On a more serious note, the opposite of 𝒮e𝓉 is clearly 𝓉ℯ𝒮 haha —technically for the purposes of this pun we identify the words ‘opposite’ and ‘reverse’.
For a poset (viewed as a category), its opposite is the ‘dual poset’: \((P, ⊑)ᵒᵖ \;=\; (P, ⊒)\).
In particular, the ‘least upper bound’, or ‘supremum’ in \((P, ⊑)\) of two elements \(x,y\) is an element \(s\) with the ‘universal property’: \(∀ z •\; x ⊑ z ∧ y ⊑ z \;≡\; s ⊑ z\). However, switching ⊑ with ⊒ gives us the notion of ‘infimum’, ‘greatest upper bound’! So any theorems about supremums automatically hold for infimums since the infifum is nothing more than the supremum in the dual category of the poset.
It is not difficult to see that this idea of “2 for the price of 1” for theorems holds for all categories.
FinBoolAlg ≃ FinSets ᵒᵖ
, witnessed by considering the collection of
atoms of a Boolean Algebra in one direction and the power set in the other.
Finiteness can be removed at the cost of completeness and atomicitiy,
CompleteAtomicBoolAlg ≃ 𝒮ℯ𝓉 ᵒᵖ
.
Speaking of functors, we can change the type of a functor by ᵒᵖ
ing its source and target,
while leaving it alone,
 this only changes type opify : ∀ {i j i’ j’} {𝒞 : Category {i} {j}} {𝒟 : Category {i’} {j’}} → Functor 𝒞 𝒟 → Functor (𝒞 ᵒᵖ) (𝒟 ᵒᵖ) opify F = record { obj = obj F ; mor = mor F ; id = Functor.id F ; comp = Functor.comp F }
Category Theory is the ‘op’ium of the people!
— Karl Marx might say it had cats existed in his time
This two definitions seem to indicate that we have some form of oppositefunctor … ;) —keep reading!
opify
seems to show that Functor 𝒞 𝒟 ≡ Functor (𝒞 ᵒᵖ) (𝒟 ᵒᵖ)
, or alternatively a
functor can have ‘two different types’ —this is akin to using the integers as reals
without writing out the inclusion formally, leaving it implicit; however, in the Agda mechanization
everything must be made explicit —the type system doesn’t let you get away with such things.
Professor Maarten Fokkinga has informed me that
the formalization allowing multipletypes is called a
precategory.
With 𝒞𝒶𝓉
inhand, we can formalise the opposite, or ∂ual, functor:
∂ : ∀ {i j} → Functor (𝒞𝒶𝓉 {i} {j}) 𝒞𝒶𝓉 ∂ = record { obj = _ᵒᵖ ; mor = opify ; id = ≡refl ; comp = ≡refl }
Conjecture: Assuming categories are equipped with a contravariant involutionary functor that is identity on objects, we can show that the identity functor is naturally isomorphic to the opposite functor.
ahyeah : ∀ {i j} (let Cat = Obj (𝒞𝒶𝓉 {i} {j}))  identity on objects cofunctor, sometimes denoted _˘ → (dual : ∀ (𝒞 : Cat) {x y : Obj 𝒞} → x ⟶ y ∶ 𝒞 → y ⟶ x ∶ 𝒞) → (Id˘ : ∀ ⦃ 𝒞 : Cat ⦄ {x : Obj 𝒞} → dual 𝒞 Id ≡ Id {A = x}) → (⨾˘ : ∀ ⦃ 𝒞 : Cat ⦄ {x y z : Obj 𝒞} {f : x ⟶ y} {g : y ⟶ z} → dual 𝒞 (f ⨾ g ∶ 𝒞) ≡ (dual 𝒞 g) ⨾ (dual 𝒞 f) ∶ 𝒞)  which is involutionary → (˘˘ : ∀ ⦃ 𝒞 : Cat ⦄ {x y : Obj 𝒞} {f : x ⟶ y} → dual 𝒞 (dual 𝒞 f) ≡ f)  which is respected by other functors → (respect : ⦃ 𝒞 𝒟 : Cat ⦄ {F : 𝒞 ⟶ 𝒟} {x y : Obj 𝒞} {f : x ⟶ y} → mor F (dual 𝒞 f) ≡ dual 𝒟 (mor F f))  then → ∂ ≅ Id within Func (𝒞𝒶𝓉 {i} {j}) 𝒞𝒶𝓉
ahyeah = {! exercise !}
Some things to note.
Categories whose morphisms are all isomorphisms are called ‘groupoids’ —groups are just oneobject groupoids. Consequently, restricted to groupoids the opposite functor is naturally isomorphic to the identity functor!
In fact, the group case was the motivator for me to conjecture the theorem, which took a while to prove since I hadn’t
a clue what I needed to assume. Here we’d use a ˘ ≔ a ⁻¹
.
Consider the category Rel
whose objects are sets and whose morphisms are ‘typedrelations’ \((S, R, T)\),
where \(R\) is a relation from set \(S\) to set \(T\), and
composition is just relational composition
—the notion of ‘untyped’, or multityped, morphisms is formalized as precategories;
see Fokkinga.
Then we can define an endofunctor by taking ˘
to be relational converse: \(x \,(R ˘)\, y \;≡\; y \,R\, x\).
Consequently, restricted to the category Rel
we have that the opposite functor is naturally isomorphic to the identity functor.
The above items are instance of a more general concept, of course.
A category with an involutionary contravariant endofunctor that is the identity on objects
is known as a dagger category, an involutive/star category, or a category with converse
—and the functor is denoted as a superscript suffix by †, *, ˘
, respectively.
The dagger notation probably comes from
the Hilbert space setting while the converse notation comes from the relation algebra setting.
As far as I know, the first two names are more widely known.
A dagger category bridges the gap between arbitrary categories and groupoids.
Just as matrices with matrix multiplication do not form a monoid but rather a category, we have that not all matrices are invertible but they all admit transposition and so we have a dagger category. In the same vein, relations admit converse and so give rise to a category with converse.
Besides relations and groupoids, other examples include:
Spoilers!! Just as the category of categories is carestian closed, so too is the category of dagger
categories and dagger preserving functors –c.f.,the respect
premise above.
For any two categories 𝒞 and 𝒟 we can construct their ‘product’ category \(𝒞 ⊗ 𝒟\) whose objects and morphisms are pairs with components from 𝒞 and 𝒟: \(\Obj\, (𝒞 ⊗ 𝒟) \;\;=\;\; \Obj\, 𝒞 \,×\, \Obj\, 𝒟\) and \((A , X) ⟶_{𝒞 ⊗ 𝒟} (B , Y) \;\;=\;\; (A ⟶_𝒞 B) \,×\, (X ⟶_𝒟 Y)\).
 we cannot overload symbols in Agda and so using ‘⊗’ inplace of more common ‘×’. _⊗_ : ∀ {i j i’ j’} → Category {i} {j} → Category {i’} {j’} → Category 𝒞 ⊗ 𝒟 = {! exercise !}
Observe that in weaker languages, a category is specified by its objects, morphisms, and composition —the proof obligations are delegated to comments, if they are realized at all. In such settings, one would need to prove that this construction actually produces a fullfledged category. Even worse, this proof may be a distance away in some documentation. With dependent types, our proof obligation is nothing more than another component of the program, a piece of the category type.
In a similar fashion we can show that the sum of two categories is again a category and in general
we have the same for quantified variants: Π 𝒞 ∶ Family • 𝒞
, likewise for ‘Σ’.
For the empty family, the empty sum yields the category 𝟘
with no objects and
the empty product yields the category 𝟙
of one object.
One can then show the usual ‘laws of arithmetic’ —i.e., ×,+ form a commutative monoid, up to isomorphism—
hold in this setting: Letting ★ ∈ {+,×}
, we have
associtivity A ★ (B ★ C) ≅ (A ★ B) ★ C
, symmetry A ★ B ≅ B ★ A
,
unit 𝟙 × A ≅ 𝟘 + A ≅ A
, and zero 𝟘 × A ≅ 𝟘
.
These notions can be defined for any category though the objects may or may not exist
— in 𝒮e𝓉
and 𝒢𝓇𝒶𝓅𝒽
, for example, they do exist ;) —and these associated arithmetical
laws also hold.
Question! What of the distributivity law,
A × (B + C) ≅ (A × B) + (A × C)
, does it hold in the mentioned cases?
Let 𝒫𝒮e𝓉
be the category of sets with a distinguished point, i.e., Σ S : Obj 𝒮e𝓉 • S
, and
functions that preserve the ‘point’, one can then show —if he or she so desires, and is not
lazy— that this category has notions of product and sum but distributivity fails.
Some interpretations:
As expected, we have projections,
Fst : ∀ {i j i’ j’} {𝒞 : Category {i} {j}} {𝒟 : Category {i’} {j’}} → Functor (𝒞 ⊗ 𝒟) 𝒞 Fst = record { obj = proj₁ ; mor = proj₁ ; id = ≡refl ; comp = ≡refl } Snd : ∀ {i j i’ j’} {𝒞 : Category {i} {j}} {𝒟 : Category {i’} {j’}} → Functor (𝒞 ⊗ 𝒟) 𝒟 Snd = record { obj = proj₂ ; mor = proj₂ ; id = ≡refl ; comp = ≡refl }
For types we have \[ (𝒳 × 𝒴 ⟶ 𝒵) \quad≅\quad (𝒳 ⟶ 𝒵 ^ 𝒴) \quad≅\quad (𝒴 ⟶ 𝒵 ^ 𝒳)\] Since categories are essentially types endowed with nifty structure, we expect it to hold in that context as well.
 Everyone usually proves currying in the first argument,  let’s rebel and do so for the second argument curry₂ : ∀ {ix jx iy jy iz jz} {𝒳 : Category {ix} {jx}} {𝒴 : Category {iy} {jy}} {𝒵 : Category {iz} {jz}} → Functor (𝒳 ⊗ 𝒴) 𝒵 → Functor 𝒴 (Func 𝒳 𝒵) curry₂ = {! exercise !}
Just as addition can be extended to numbervalued functions pointwise, \(f + g \;≔\; λ x → f x + g x\), we can do the same thing with functors.
 For bifunctor ‘⊕’ and functors ‘F, G’, we have a functor ‘λ x → F x ⊕ G x’ pointwise : ∀ {ic jc id jd ix jx iy jy} {𝒞 : Category {ic} {jc}} {𝒟 : Category {id} {jd}} {𝒳 : Category {ix} {jx}} {𝒴 : Category {iy} {jy}} → Functor (𝒳 ⊗ 𝒴) 𝒟 → Functor 𝒞 𝒳 → Functor 𝒞 𝒴 → Functor 𝒞 𝒟 pointwise = {! exercise !}
By ‘extensionality’ p ≡ (proj₁ p , proj₂ p)
, we have that the pointwise extension along the projections
is the orginal operation.
exempligratia : ∀ {𝒞 𝒟 𝒳 𝒴 : Category {ℓ₀} {ℓ₀}} (⊕ : Functor (𝒳 ⊗ 𝒴) 𝒟) → let _⟨⊕⟩_ = pointwise ⊕ in Fst ⟨⊕⟩ Snd ≡ ⊕ exempligratia Bi = funcext (≡cong (obj Bi) ≡refl) (≡cong (mor Bi) ≡refl)
An example bifunctor is obtained by extending the ‘⟶’ to morphisms:
Given f : A ⟶ B , g : C ⟶ D
we define (f ⟶ g) : (B ⟶ C) → (A ⟶ C)
by
λ h → f ⨾ h ⨾ g
as this is the only way to define it so as to meet the type requirements.
Hom : ∀ {i j} {𝒞 : Category {i} {j} } → Functor (𝒞 ᵒᵖ ⊗ 𝒞) (𝒮e𝓉 {j})  hence contravariant in ‘first arg’ and covaraint in ‘second arg’ Hom {𝒞 = 𝒞} = let module 𝒞 = Category 𝒞 instance 𝒞′ : Category ; 𝒞′ = 𝒞 ⨾cong₂ : ∀ {A B C : Obj 𝒞} {f : A 𝒞.⟶ B} {g g’ : B 𝒞.⟶ C} → g ≡ g’ → f 𝒞.⨾ g ≡ f 𝒞.⨾ g’ ⨾cong₂ q = ≡cong₂ 𝒞._⨾_ ≡refl q in record { obj = λ{ (A , B) → A ⟶ B } ; mor = λ{ (f , g) → λ h → f ⨾ h ⨾ g } ; id = extensionality (λ {h} → begin Id 𝒞.⨾ h 𝒞.⨾ Id ≡⟨ leftId ⟩ h 𝒞.⨾ Id ≡⟨ rightId ⟩ h ∎) ; comp = λ {x y z fg fg’} → let (f , g) = fg ; (f’ , g’) = fg’ in extensionality (λ {h} → begin (f’ 𝒞.⨾ f) 𝒞.⨾ h 𝒞.⨾ (g 𝒞.⨾ g’) ≡⟨ assoc ⟩ f’ 𝒞.⨾ (f 𝒞.⨾ (h 𝒞.⨾ (g 𝒞.⨾ g’))) ≡⟨ ⨾cong₂ (≡sym assoc) ⟩ f’ 𝒞.⨾ ((f 𝒞.⨾ h) 𝒞.⨾ (g 𝒞.⨾ g’)) ≡⟨ ⨾cong₂ (≡sym assoc) ⟩ f’ 𝒞.⨾ ((f 𝒞.⨾ h) 𝒞.⨾ g) 𝒞.⨾ g’ ≡⟨ ⨾cong₂ (≡cong₂ 𝒞._⨾_ assoc ≡refl) ⟩ f’ 𝒞.⨾ (f 𝒞.⨾ h 𝒞.⨾ g) 𝒞.⨾ g’ ∎) }
The naming probably comes from the algebra/monoid case where the functors are
monoid hom
omorphisms. Some prefer to use the name Mor
, short for mor
phisms,
and that’s cool too. While Haskell programmers might call this the Reader
functor.
Usual notation for this functor is Hom
, but I like Fokkinga’s much better.
He uses (_⟶_)
and writes (f ⟶ g) = λ h • f ⨾ h ⨾ g
—the first argument of Hom is the first argument of the composition and the last
argument to Hom is the last argument of the resulting composition :)
One way is to make it so 𝒮imple that there are obviously no deficiencies, and the other way is to make it so 𝒞omplicated that there are no obvious deficiencies. The first method is far more difficult. It demands the same skill, devotion, insight, and even inspiration as the discovery of the simple physical laws which 𝒰nderlie the complex phenomena of nature.
( The 𝒞omplex philosophy behinds games such as Chess and Go arise from some 𝒮imple board game rules. )
In this section we discuss what it means to be a ‘forgetful functor’? –Also called an `𝒰nderlying functor'.
The modifier ‘forgetful’ is meaningful when there’s a notion of extra structure. Indeed any functor F : 𝒞 ⟶ 𝒮 can be thought of as forgetful by construing the objects of 𝒞 as objects of 𝒮 with extra structure. Mostly: You know it (to be forgetful) when you see it!
A common example from set theory is the ‘inclusion’ of a subset \(A\) of \(B\), the injection \(ι : A ↪ B : a ↦ a\) —it is essentially a form of ‘type casting’: \(a ∈ A\) and \(ι a \;=\; a ∈ B\). Such injections ‘forget’ the property that the argument is actually a member of a specified subset. Indeed, construing sets as categories then functions becomes functors and inclusions are then forgetful functors!
Since a functor F consists of two maps (F₀, F₁) ≔ (obj F, mor F) and some properties, we can speak about properties of the functor and about properties of either of its maps. The common definitions are a functor \(F\) is:
Now we can generalize the previous example. Every faithful functor F : 𝒞 ⟶ 𝒟 can be construed as forgetful: The 𝒞maps can be embedded into the 𝒟maps, since F is faithful, and so can be thought of as a special subcollection of the 𝒟maps; then \(F\) ‘forgets’ the property of being in this special subcollection.
Are faithful functors in abundance? Well any functor forgetting only axioms (and/or structure) is faithful:
Now given, \(F (f , prf) = F (g , prf) \;⇔\; f = g \;⇔\; (f , prf) = (g , prf)\) – equality does not (extensionally) depend on proof components.
Hence, faithful :)
(Likewise for forgetting extra structure).
Of course we’re not saying all forgetful functors are necessarily faithful. A simple counterexample is the absolute value function: Given a real number \(x\) it’s absolute value \(∣x∣\) is obtained by totally ignoring its sign —of course \(x\) and \(∣x∣\) are equidistant from 0, the relation equidistantfrom0 is an equivalence relation –Exercise!–, and so the the two are isomorphic in some sense.
Motivated by this, given a set \(S\) it’s size is denoted \(∣ S ∣\) which totally forgets about the elements of the set —of course it can be shown that two sets are isomorphic precisely if they are equinumerous.
I assume it is with these as motivators, some people write \(∣·∣\) for a forgetful functor.
( Exercise: A functor F : 𝒞 ≃ 𝒟
is (part of) an equivalence iff it is full,
faithful, and ‘‘essentially surjective on objects’’:
∀ D : Obj 𝒟 • Σ C : Obj 𝒞 • F C ≅ D
—note the iso. )
If you’ve ever studied abstract algebra —the math with vector spaces— then you may recall that a collection of vectors ℬ is called a ‘basis’ if every vector can be written as a linear combination of these vectors: For any vector \(v\), there are scalars \(c₁, …, cₙ\) and vectors \(b₁, …, bₙ\) in ℬ with \(v \;=\; c₁·b₁ + ⋯ + cₙ·bₙ\). That is, a basis is a collection of ‘building blocks’ for the vector space. Then any function \(f\) between basis sets immediately lifts to a linear transformation (think vector space morphism) \(F\) as follows: Given a vector \(v\), since we have a basis, we can express it as \(c₁·b₁ + ⋯ + cₙ·bₙ\), now define \(F v \;≔\; c₁·(f\, b₁) + ⋯ + cₙ·(f\, bₙ)\).
Sweet!
Thus, to define a complicated linear transformation of vector spaces, it more than suffices to define a plain old simple function of basis sets. Moreover, by definition, such \(F\) maps basis vectors to basis vectors: \(f \;=\; ι ⨾ F\) where \(ι : ℬ ↪ 𝒱\) is the inclusion that realises basis vectors as just usual vectors in the vector space 𝒱. Slogan: Vector space maps are determined by where they send their basis, and basisvectors are preserved.
In the case of (List A, ++, [])
we may consider A
to be a ‘basis’ of the monoid —indeed,
every list can be written as a linear combination of elements of A
, given list
[x₁, …, xₙ] : List A
we have [x₁, …, xₙ] = x₁ + ⋯ + xₙ
where x + y ≔ [x] ++ [y]
.
Reasoning similarly as above, or if you have familiarity with foldr , reduce
, we have a slogan:
Monoid homomorphisms from lists are determined by where they send their basis and basisvectors are preserved.
Now the general case: \(F ⊣ U\) is a (freeforgetful) ‘adjunction’ means for functors ‘forget’ \(U : 𝒞 ⟶ 𝒮\) and ‘free’ \(F : 𝒮 → 𝒞\), we have that for a given 𝒮impleobject \(S\) there’s 𝒮implemap \(ι : S ⟶_𝒮 U\,(F\, S)\) —a way to realise ‘basis vectors’— such that for any 𝒞omplicatedobject \(C\) and 𝒮implemaps \(φ : S ⟶_𝒮 U\, C\), there is a unique 𝒞omplicatedmap \(Φ : F\, S ⟶_𝒞 C\) that preserves the basis vectors: \(φ = ι ⨾ U Φ\).
By analogy to the previous two cases, we may consider \(U\, X\) to be a ‘basis’, and make the slogan: 𝒞omplicatedmaps from free objects are determined by where they send their basis and ‘basis vectors’ are preserved.
[ In more categorical lingo, one says \(ι\) is the ‘insertion of generators’.
Question: Does the way we took \(ι\) in the previous graph show that it is a natural transformation \(ι : \Id ⟶ F ⨾ U\)? —The naturality just says that a ‘homomorphism’ \(F f\) on the free object is completely determined by what \(f\) does to the generators ;) ]
An adjunction \(L ⊣ U\), where the L
ower adjoint is from 𝒮 to 𝒞 and the U
pper adjoint is in
the opposite direction, lends itself to an elemntary interpretation if we consider 𝒞
to be some universe of 𝒞omplicated items of study, while 𝒮 to be a universe of 𝒮imple
items of study. Then adjointness implies that given a simpleobject \(S\) and a complicatedobject
\(C\), a simplemap \(X ⟶ U\, C\) corresponds to a complicatedmap \(L\, S ⟶ C\). To work with
complicatedmaps it is more than enough to work with simplemaps!
Formally this correspondence, saying \(F : 𝒞 ⟶ 𝒟\) is adjoint to \(G : 𝒟 ⟶ 𝒞\), written \(F ⊣ G\), holds precisely when \((F ∘ X ⟶ Y) \;≅\; (X ⟶ G ∘ Y)\) in a functor category:
_⊣₀_ : ∀ {i j} {𝒞 𝒟 : Category {i} {j}} → Functor 𝒞 𝒟 → Functor 𝒟 𝒞 → Set (i ⊍ j) _⊣₀_ {𝒞 = 𝒞} {𝒟} F G = (F ′ ∘ X ⟶ₙₐₜ Y) ≅ (X ⟶ₙₐₜ G ∘ Y) within Func (𝒞 ᵒᵖ ⊗ 𝒟) 𝒮e𝓉 where X = Fst ; Y = Snd ; _′ = opify  only changes types infix 5 _⟶ₙₐₜ_ _⟶ₙₐₜ_ : ∀ {i j} {𝒜 : Category {i} {j}} → Functor (𝒞 ᵒᵖ ⊗ 𝒟) (𝒜 ᵒᵖ) → Functor (𝒞 ᵒᵖ ⊗ 𝒟) 𝒜 → Functor (𝒞 ᵒᵖ ⊗ 𝒟) 𝒮e𝓉 _⟶ₙₐₜ_ {i} {j} {𝒜} = pointwise (Hom {i} {j} {𝒜})
Note that if we use Agda's builtin rewrite mechanism to add the rule,
{𝒞 𝒟 : Category {ℓ₀} {ℓ₀}} → Functor (𝒞 ᵒᵖ) (𝒟 ᵒᵖ) ≡ Functor 𝒞 𝒟
then we might be able to get away without using opify
.
Anyhow, this says for any objects \(X\) and \(Y\), the collection of morphisms \((F\, A ⟶ B)\) is isomorphic to the collection \((A ⟶ G\, B)\) and naturally so in \(A\) and \(B\).
Unfolding it, we have
record _⊣_ {i j i’ j’} {𝒞 : Category {i} {j}} {𝒟 : Category {i’} {j’}} (F : Functor 𝒞 𝒟) (G : Functor 𝒟 𝒞) : Set (j’ ⊍ i’ ⊍ j ⊍ i) where open Category 𝒟 renaming (_⨾_ to _⨾₂_) open Category 𝒞 renaming (_⨾_ to _⨾₁_) field  ‘leftadjunct’ L ≈ ⌊ and ‘rightadjunct’ r ≈ ⌈ ⌊_⌋ : ∀ {X Y} → obj F X ⟶ Y ∶ 𝒟 → X ⟶ obj G Y ∶ 𝒞 ⌈_⌉ : ∀ {X Y} → X ⟶ obj G Y ∶ 𝒞 → obj F X ⟶ Y ∶ 𝒟  Adjuncts are inverse operations lid : ∀ {X Y} {d : obj F X ⟶ Y ∶ 𝒟} → ⌈ ⌊ d ⌋ ⌉ ≡ d rid : ∀ {X Y} {c : X ⟶ obj G Y ∶ 𝒞} → ⌊ ⌈ c ⌉ ⌋ ≡ c  That for a fixed argument, are natural transformations between Hom functors lfusion : ∀ {A B C D} {f : A ⟶ B ∶ 𝒞} {ψ : obj F B ⟶ C ∶ 𝒟} {g : C ⟶ D ∶ 𝒟} → ⌊ mor F f ⨾₂ ψ ⨾₂ g ⌋ ≡ f ⨾₁ ⌊ ψ ⌋ ⨾₁ mor G g rfusion : ∀ {A B C D} {f : A ⟶ B ∶ 𝒞} {ψ : B ⟶ obj G C ∶ 𝒞} {g : C ⟶ D ∶ 𝒟} → ⌈ f ⨾₁ ψ ⨾₁ mor G g ⌉ ≡ mor F f ⨾₂ ⌈ ψ ⌉ ⨾₂ g
This is easier for verifying an adjunction, while the former is easier for remembering and understanding what an adjunction actually is.
As the slogan goes ‘adjunctions are everywhere’. They can be said to capture the notions of optimization and efficiency, but also that of simplicity.
For example, the supremum of a function is defined to be an upper bound of its image set and the least such bound. Formally, this definition carries a few quantifiers and so a bit lengthy. More elegantly, we can say the supremum operation is leftadjoint to the constant function: \[ \mathsf{sup} ⊣ 𝒦 \] which means \[ ∀ z •\qquad \mathsf{sup}\, f \,≤\, z \quad⇔\quad f \overset{.}{≤} 𝒦\, z\] Where \(𝒦\, x\, y \,=\, x\) and the \(\overset{.}{≤}\) on the right is the pointwise ordering on functions. This formulation of supremum is not only shorter to write but easier to use in calculational proofs.
For the efficiency bit, recall that it is efficient to specify a 𝒮implemap, then use the adjuction, to obtain a 𝒞omplicatedmap. Recall in the last paragraph how we define the super complicated notion of supremum of a function in terms of the most elementary constant function!
Adjunctions over poset categories are called ‘Galois connections’ and a good wealth of material on them can be found in nearly any writing by Backhouse et. al., while a very accessible introduction is by Aarts, and there is also an Agda mechanisation by Kahl & Alhassy.
Regarding forgetful functors: Generally, but not always, forgetful functors are faithful and have left adjoints —because the notion of ‘forget’ ought to have a corresponding notion of ‘free’. An exception to this is the category of fields, which has a forgetful functor to the category of sets with no left adjoint.
Another awesome thing about adjunctions L ⊣ U
is that they give us ‘representable functors’,
a.k.a. ‘the best kind of functors’, when terminal objects exist.
𝟙
is ‘terminal’ if for any object A
there is a unique morphism ! {A} : A ⟶ 𝟙
.
In 𝒮ℯ𝓉 we have (A ⟶ 𝟙) ≅ 𝟙
and (𝟙 ⟶ A) ≅ A
.U : 𝒞 ⟶ 𝒮e𝓉
, to
a given set A
and 𝟙
we obtain (L 𝟙 ⟶ A) ≅ (𝟙 ⟶ U A) ≅ U A
and so one says
‘ U
is represented by L 𝟙
’.A
then it suffices to utilise the maps L 𝟙 ⟶ A
.
In the case of a freeforgetful adjunction, this says that
a forgetful functor is represented by the free object with generator 𝟙
.
For example, for monoids the onepoint monoid is the terminal object: 𝟙 ≔ ({*}, ⊕, *)
with x ⊕ y ≔ ⋆
.
Then every monoidhomomorphism from 𝟙
just picks out an element of the carrier of a monoid and so
(𝟙 ⟶ M) ≅ 𝒰 M
where 𝒰
is the forgetful functor for monoids mentioned in the introduction.
A final note about ‘free objects’ —arising from an adjoint to a forgetful functor.
‘‘The free object is generic’’: The only truths provable for the free object are precisely those that hold for every complicatedobject.
(Begin squinting eyes)
This follows from the
definition of adjunction which says we can construct a unique morphism between complicatedobjects
from a simplemap and by naturality we may transport any proof for the free object to any
complicated object.
(Feel ‘free’ to stop squinting your eyes)
For futher reading consider reading the adjoint article at the comic book library and for more on the adjective ‘forgetful’ see ncatlab or mathworld A nice list of common free objects can be found on wikipedia.
You might be asking, musa, when am I ever going to encounter this in daily life? In a popular setting? This concept is everywhere, even inclusions as mentioned earlier are an instance. For the second question, enjoy listening to this lovely musical group –they use the words ‘forgetful functors’ ;)
The remainder of this document can be seen as one fullyworked out example of constructing a free functor for the forgetful 𝒰 defined above from 𝒞𝒶𝓉 to 𝒢𝓇𝒶𝓅𝒽.
The “right” definition is hard to obtain!
We can now define a ‘path’ of length n
in a graph G
to be a graphmap
[ n ] ⟶ G
.
Path₀ : ℕ → Graph₀ → Set (ℓsuc ℓ₀) Path₀ n G = [ n ]₀ 𝒢⟶₀ G
Unfolding the definition of graphmorphisms, this just says that a path of length n
consists of a sequence [v₀, v₁, v₂, …, vₙ]
of vertices of G
and a sequence [e₀, e₁, …, eₙ₋₁]
of edges of G
with typing eᵢ : vᵢ ⟶ vᵢ₊₁
.
The definition is pretty slick! However, as the name suggests, perhaps we can concatenate paths and it’s not at all clear how to do this for the vertex and edge morphisms of the graphmaps involved, whereas it’s immediately clear how to do this with sequences: We just concatenate the sequences and ensure the result is coherent.
Since the vertices can be obtained from the edges via src
and tgt
, we can dispense with them
and use the definition as outlined above.
open import Data.Vec using (Vec ; lookup) record Path₁ (n : ℕ) (G : Graph₀) : Set (ℓsuc ℓ₀) where open Graph₀ field edges : Vec (E G) (suc n) coherency : {i : Fin n} → tgt G (lookup (` i) edges) ≡ src G (lookup (fsuc i) edges)
That is, edges [e₀, …, eₙ]
with coherency tgt eᵢ ≡ src eᵢ₊₁
.
Great, we’ve cut the definition of Path₀
in half but that fact that we get a raw list of edges
and then need coherency to ensure that it is a wellformed path is still not terribly lovely.
After all, we’re in Agda, we’re among kings, we should be able to form the list in such a way that
the end result is a path. Let’s do that!
Enough of this repetition, let us fix a graph G
,
module Pathdefinition2 (G : Graph₀) where open Graph₀ G mutual data Path₂ : Set where _! : V → Path₂ cons : (v : V) (e : E) (ps : Path₂) (s : v ≡ src e) (t : tgt e ≡ head₂ ps) → Path₂ head₂ : Path₂ → V head₂ (v !) = v head₂ (cons v e p s t) = v
Defining paths for the parallelpair approach to graphs leaves us with the need to carry proofs around, and this is a tad too clunky in this case. Let's try yet again.
module Pathdefinition3 (G : Graph) where open Graph G  handy dandy syntax infixr 5 cons syntax cons v ps e = v ⟶[ e ]⟶ ps  v goes, by e, onto path ps  we want wellformed paths mutual data Path₃ : Set where _! : (v : V) → Path₃ cons : (v : V) (ps : Path₃) (e : v ⟶ head₃ ps) → Path₃ head₃ : Path₃ → V head₃ (v !) = v head₃ (v ⟶[ e ]⟶ ps) = v  motivation for the syntax declaration above example : (v₁ v₂ v₃ : V) (e₁ : v₁ ⟶ v₂) (e₂ : v₂ ⟶ v₃) → Path₃ example v₁ v₂ v₃ e₁ e₂ = v₁ ⟶[ e₁ ]⟶ v₂ ⟶[ e₂ ]⟶ v₃ ! end₃ : Path₃ → V end₃ (v !) = v end₃ (v ⟶[ e ]⟶ ps) = end₃ ps  typed paths; squigarrowright record _⇝_ (x y : V) : Set where field path : Path₃ start : head₃ path ≡ x finish : end₃ path ≡ y
This seems great, but there’s always room for improvement:
Since the cons
constructor's third argument depends on its first, we must
use a syntax declaration to get the desired look. Such aesthetic is not only
pleasing but reminiscent of diagrammatic paths;
moreover, it’s guaranteed to be an actual path and not just an
alternating lists of vertices and edges.
Using the clunky Path₂
, we’d write
v₁ ⟶[ v₁≈se₁ , e₁ , te₁≈v₂ ]⟶ v₂ ⟶[ v₂≈se₂ , e₂ , te₂≈v₃ ]⟶ v₃ ! where syntax cons v e ps s t = v ⟶[ s , e , t ]⟶ ps
yuck!
Finally, the syntaxdeclaration does not make the emacs agdamode autocase using the syntax, and so I have to write it out by hand, each time I want to use the syntax.
cons
's third argument depends on the second argument, we need a mutual
definition to extract the item of the dependence. Perhaps if we embed this item at
the type level we may avoid the need of an auxiliary mutuallydefined function.Graph₀
, which we argued is less preferable to the typedapproach to graphs.
Perhaps defining paths with types by default, we can reap the benefits and simplicity
of the typedapproach to graphs.module TypedPaths (G : Graph) where open Graph G hiding(V) open Graph using (V) data _⇝_ : V G → V G → Set where _! : ∀ x → x ⇝ x _⟶[_]⟶_ : ∀ x {y ω} (e : x ⟶ y) (ps : y ⇝ ω) → x ⇝ ω
One might think that since we can write
src : {x y : V G} (e : x ⟶ y) → V G src {x} {y} e = x
we can again ignore vertices and it suffices to just keep a coherent list of edges. Then what is an empty path at a vertex? This’ enough to keep vertices around —moreover, the ensuing terms look like diagrammatic paths! Cool!
Finding this definitional form was a major hurdle in this endeavour.
With paths in hand, we can now consider a neat sequence of exercises :)
Show that graphmaps preserve paths: (f : G ⟶ H) → x ⇝ y → fᵥ x ⇝ fᵥ y
;
this is nothing more than typepreservation for f
to be a functor 𝒫G ⟶ 𝒫H
;)
Hint: This is lift
from the next section.
Define
a connected b ≡ (a ⇝ b) ⊎ (b ⇝ a)  path “between” a and b; not ‘from a to b’.
𝒦G
.𝒞
, define 𝒦 𝒞 ≔ 𝒦 (𝒰₀ 𝒞)
which is a subcategory of 𝒞
.𝒦f : 𝒦G ⟶ 𝒦H : (connected component of x) ↦ (connected component of fᵥ x)
.𝒦 : Graph ⟶ Set
.
Then there is a natural transformation 𝒱 ⟶ 𝒦
, where 𝒱 is the vertices functor.
Hint: Such a transformation means we can realise vertices as connected components and this suggests taking assigning a vertex to the connectedcomponent block that contains it.
yeah!
Finally, if we let 𝒟 : 𝒮ℯ𝓉 → 𝒞𝒶𝓉
be the free category functor that associates each set with
the discrete category over it, then we have 𝒦
is the associated forgetful functor.
Here's a handydandy combinator for forming certain equality proofs of paths.
 Preprend preserves path equality ⟶≡ : ∀{x y ω} {e : x ⟶ y} {ps qs : y ⇝ ω} → ps ≡ qs → (x ⟶[ e ]⟶ ps) ≡ (x ⟶[ e ]⟶ qs) ⟶≡ {x} {y} {ω} {e} {ps} {qs} eq = ≡cong (λ r → x ⟶[ e ]⟶ r) eq
Less usefully, we leave as exercises:
edges : ∀ {x ω} (p : x ⇝ ω) → List (Σ s ∶ V G • Σ t ∶ V G • s ⟶ t) edges = {! exercise !} patheq : ∀ {x y} {p q : x ⇝ y} → edges p ≡ edges q → p ≡ q patheq = {! exercise !}
Given time, patheq
could be rewritten so as to be more easily applicable.
For now, two path equality proofs occur in the document and both are realised by
quickandeasy induction.
Now we turn back to the problem of catenating two paths.
infixr 5 _++_ _++_ : ∀{x y z} → x ⇝ y → y ⇝ z → x ⇝ z x ! ++ q = q  left unit (x ⟶[ e ]⟶ p) ++ q = x ⟶[ e ]⟶ (p ++ q)  mutualassociativity
Notice that the the base case indicate that !
forms a leftunit for ++
,
while the inductive case says that pathformation associates with path catenation.
Both observations also hold for the definition of list catenation ;)
If we had not typed our paths, as in Path₂
, we would need to carry around a
proof that paths are compatible for concatenation:
catenate : (p q : Path) (coh : end p ≡ head q) → Path syntax catenate p q compatibility = p ++[ compatibility ] q
Even worse, to show p ++[ coh ] q ≡ p ++[ coh’ ] q
we need to invoke proofirrelevance of
identity proofs to obtain coh ≡ coh’
, each time we want such an equality! Moving the proof
obligation to the type level removes this need.
As can be seen, being typeless is a terrible ordeal.
Just as the first clause of _++_
indicates _!
is a left unit,
leftId : ∀ {x y} {p : x ⇝ y} → x ! ++ p ≡ p leftId = ≡refl
Is it also a right identity?
rightId : ∀ {x y} {p : x ⇝ y} → p ++ y ! ≡ p rightId {x} {.x} {.x !} = ≡refl rightId {x} {y } {.x ⟶[ e ]⟶ ps} = ≡cong (λ q → x ⟶[ e ]⟶ q) rightId
Is this operation associative?
assoc : ∀{x y z ω} {p : x ⇝ y} {q : y ⇝ z} {r : z ⇝ ω} → (p ++ q) ++ r ≡ p ++ (q ++ r) assoc {x} {p = .x !} = ≡refl assoc {x} {p = .x ⟶[ e ]⟶ ps} {q} {r} = ≡cong (λ s → x ⟶[ e ]⟶ s) (assoc {p = ps})
Hence, we’ve shown that the paths over a graph G
constitute a category! Let’s call it 𝒫 G
.
In the last section, we showed that the paths over a graph make a category,
𝒫₀ : Graph → Category 𝒫₀ G = let open TypedPaths G in record { Obj = Graph.V G ; _⟶_ = _⇝_ ; _⨾_ = _++_ ; assoc = λ {x y z ω p q r} → assoc {p = p} ; Id = λ {x} → x ! ; leftId = leftId ; rightId = rightId }
Can we make 𝒫
into a functor by defining it on morphisms?
That is, to lift graphmaps to categorymaps, i.e., functors.
𝒫₁ : ∀ {G H} → GraphMap G H → Functor (𝒫₀ G) (𝒫₀ H) 𝒫₁ {G} {H} f = record { obj = ver f ; mor = amore ; id = ≡refl ; comp = λ {x} {y} {z} {p} → comp {p = p} } where open TypedPaths ⦃...⦄ public instance G' : Graph ; G' = G H' : Graph ; H' = H amore : {x y : Graph.V G} → x ⇝ y → (ver f x) ⇝ (ver f y) amore (x !) = ver f x ! amore (x ⟶[ e ]⟶ p) = ver f x ⟶[ edge f e ]⟶ amore p comp : {x y z : Graph.V G} {p : x ⇝ y} {q : y ⇝ z} → amore (p ++ q) ≡ amore p ++ amore q comp {x} {p = .x !} = ≡refl  since ! is left unit of ++ comp {x} {p = .x ⟶[ e ]⟶ ps} = ⟶≡ (comp {p = ps})
Sweet!
With these two together, we have that 𝒫
is a functor.
𝒫 : Functor 𝒢𝓇𝒶𝓅𝒽 𝒞𝒶𝓉 𝒫 = record { obj = 𝒫₀ ; mor = 𝒫₁ ; id = λ {G} → funcext ≡refl (id ⦃ G ⦄) ; comp = funcext ≡refl comp } where open TypedPaths ⦃...⦄ open Category ⦃...⦄ module 𝒞𝒶𝓉 = Category 𝒞𝒶𝓉 module 𝒢𝓇𝒶𝓅𝒽 = Category 𝒢𝓇𝒶𝓅𝒽 id : ∀ ⦃ G ⦄ {x y} {p : x ⇝ y} → mor (𝒞𝒶𝓉.Id {𝒫₀ G}) p ≡ mor (𝒫₁ (𝒢𝓇𝒶𝓅𝒽.Id)) p id {p = x !} = ≡refl id {p = x ⟶[ e ]⟶ ps} = ⟶≡ (id {p = ps}) comp : {G H K : Graph} {f : GraphMap G H} {g : GraphMap H K} → {x y : Graph.V G} {p : TypedPaths._⇝_ G x y} → mor (𝒫₁ f 𝒞𝒶𝓉.⨾ 𝒫₁ g) p ≡ mor (𝒫₁ (f 𝒢𝓇𝒶𝓅𝒽.⨾ g)) p comp {p = x !} = ≡refl comp {p = x ⟶[ e ]⟶ ps} = ⟶≡ (comp {p = ps})
It seemed prudent in this case to explicitly delimit where the compositions lives —this is for clarity, since Agda can quickly resolve the appropriate category instances.
Exercise: Show that we have a natural transformation Id ⟶ 𝒰 ∘ 𝒫
.
Free at last, free at last, thank God almighty we are free at last.
– Martin Luther King Jr.
Recall why lists give the ‘free monoid’: We can embed a type \(A\) into \(\List A\) by the map \([\_{}]\), and we can lift any map \(f : A ⟶ B\) to a monoid map \[\foldr \; (λ a b → f\, a ⊕ b)\; e \;:\; (\List A ,\_{}++\_{} , []) \,⟶\, (B,\_{}⊕\_{} , e)\] I.e., \([a₁, …, aₖ] \;↦\; f\, a₁ ⊕ ⋯ ⊕ f\, aₖ\). Moreover this ‘preserves the basis’ \(A\) – i.e., \(∀ a •\; f\, a \,=\, \foldr \,f \,e \, [ a ]\) – and this lifted map is unique.
Likewise, let us show that \(𝒫G\) is the ‘free category’ over the graph \(G\). This amounts to saying that there is a way, a graphmap, say \(ι\), that embeds \(G\) into \(𝒫G\), and a way to lift any graphmap \(f \,:\, G \,𝒢⟶\, 𝒰₀ 𝒞\) to a functor \(\mathsf{lift}\, f : 𝒫G ⟶ 𝒞\) that ‘preserves the basis’ \(f \;=\; ι ⨾ 𝒰₁ (\mathsf{lift}\, f)\) and uniquely so.
Let’s begin!
module freedom (G : Obj 𝒢𝓇𝒶𝓅𝒽) {𝒞 : Category {ℓ₀} {ℓ₀} } where open TypedPaths G using (_! ; _⟶[_]⟶_ ; _⇝_ ; _++_) open Category ⦃...⦄ module 𝒢𝓇𝒶𝓅𝒽 = Category 𝒢𝓇𝒶𝓅𝒽 module 𝒮ℯ𝓉 = Category (𝒮e𝓉 {ℓ₀}) module 𝒞 = Category 𝒞 instance 𝒞′ : Category ; 𝒞′ = 𝒞
The only obvious, and most natural, way to embed a graph into its ‘graph of paths’ is to send vertices to vertices and edges to paths of length 1.
ι : G ⟶ 𝒰₀ (𝒫₀ G) ι = record { ver = Id ; edge = λ {x} {y} e → x ⟶[ e ]⟶ (y !) }
Given a graph map \(f\), following the listanalagoue of \([a₁, …, aₖ] \;↦\; f\, a₁ ⊕ ⋯ ⊕ f\, aₖ\) we attempt to lift the map onto paths by taking the edges \(e₁, …, eₖ\) of a path to a morphism \(\edge\, f\, e₁ ⨾ ⋯ ⨾ \edge\, f\, eₖ\). That is, a path of the form \[x_0 \xrightarrow{e_1} x_1 \xrightarrow{e_2} x_2 \xrightarrow{e_3} ⋯ \xrightarrow{e_k} x_k \] Is lifted to the composition of morphisms \[\mathsf{ver}\, f\, x_0 \xrightarrow{\edge\, f\, e_1} \mathsf{ver}\, f\, x_1 \xrightarrow{\edge\, f\, e_2} \mathsf{ver}\, f\, x_2 \xrightarrow{\edge\, f\, e_3} ⋯ \xrightarrow{\edge\, f\, e_k} \mathsf{ver}\, f\, x_k \]
Of course, we then need to verify that this construction is indeed functorial.
lift : G ⟶ 𝒰₀ 𝒞 → 𝒫₀ G ⟶ 𝒞 lift f = record { obj = λ v → ver f v  Only way to obtain an object of 𝒞; hope it works! ; mor = fmap ; id = ≡refl ; comp = λ {x y z p q} → proof {x} {y} {z} {p} {q} } where fmap : ∀ {x y} → x ⇝ y → ver f x 𝒞.⟶ ver f y fmap (y !) = 𝒞.Id fmap (x ⟶[ e ]⟶ p) = edge f e 𝒞.⨾ fmap p  homomorphism property proof : ∀{x y z} {p : x ⇝ y} {q : y ⇝ z} → fmap (p ++ q) ≡ fmap p 𝒞.⨾ fmap q proof {p = ._ !} = ≡sym 𝒞.leftId proof {p = ._ ⟶[ e ]⟶ ps} = ≡cong (λ m → edge f e 𝒞.⨾ m) (proof {p = ps}) ⟨≡≡⟩ ≡sym assoc  Exercise: Rewrite this calculationally!
Now we have the embedding and the lifting, it remains to show that the aforementioned ‘preserves basis’ property holds as does uniqueness.
Let's begin with the ‘basis preservation’ property:
property : ∀{f : G ⟶ 𝒰₀ 𝒞} → f ≡ (ι 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ (lift f)) property {f} = graphmapext  Proving: ∀ {v} → ver f v ≡ ver (ι 𝒞.⨾ 𝒰₁ (lift f)) v  by starting at the complicated side and simplifying (λ {v} → ≡sym (begin ver (ι 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ (lift f)) v ≡⟨" definition of ver on composition "⟩′ (ver ι 𝒮ℯ𝓉.⨾ ver (𝒰₁ (lift f))) v ≡⟨" definition of 𝒰₁ says: ver (𝒰₁ F) = obj F "⟩′ (ver ι 𝒮ℯ𝓉.⨾ obj (lift f)) v ≡⟨" definition of lift says: obj (lift f) = ver f "⟩′ (ver ι 𝒮ℯ𝓉.⨾ ver f) v ≡⟨" definition of ι on vertices is identity "⟩′ ver f v ∎))  Proving: edge (ι ⨾g 𝒰₁ (lift f)) e ≡ edge f e (λ {x} {y} {e} → begin edge (ι 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ (lift f)) e ≡⟨" definition of edge on composition "⟩′ (edge ι 𝒮ℯ𝓉.⨾ edge (𝒰₁ (lift f))) e ≡⟨" definition of 𝒰 says: edge (𝒰₁ F) = mor F "⟩′ (edge ι 𝒮ℯ𝓉.⨾ mor (lift f)) e ≡⟨" definition chasing gives: mor (lift f) (edge ι e) = edge f e ⨾ Id "⟩′ edge f e 𝒞.⨾ Id ≡⟨ 𝒞.rightId ⟩ edge f e ∎)
Observe that we simply chased definitions and as such graphmapext ≡refl rightId
suffices as a proof,
but it’s not terribly clear why, for human consumption, and so we choose to elaborate with the
detail.
Finally, it remains to show that there is a unique way to preserve ‘basis’:
uniqueness : ∀{f : G ⟶ 𝒰₀ 𝒞} {F : 𝒫₀ G ⟶ 𝒞} → f ≡ (ι 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ F) → lift f ≡ F uniqueness {.(ι 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ F)} {F} ≡refl = funcext ≡refl (≡sym pf) where pf : ∀{x y} {p : x ⇝ y} → mor (lift (ι 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ F)) p ≡ mor F p pf {x} {.x} {p = .x !} = ≡sym (Functor.id F) pf {x} {y} {p = .x ⟶[ e ]⟶ ps} = begin mor (lift (ι 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ F)) (x ⟶[ e ]⟶ ps) ≡⟨" definition of mor on lift, the inductive clause "⟩′ edge (ι 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ F) e 𝒞.⨾ mor (lift (ι 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ F)) ps ≡⟨ ≡cong₂ 𝒞._⨾_ ≡refl (pf {p = ps}) ⟩  inductive step edge (ι 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ F) e 𝒞.⨾ mor F ps ≡⟨" definition of edge says it preserves composition "⟩′ (edge ι 𝒮ℯ𝓉.⨾ edge (𝒰₁ F)) e 𝒞.⨾ mor F ps ≡⟨" definition of 𝒰 gives: edge (𝒰₁ F) = mor F "⟩′ (edge ι 𝒮ℯ𝓉.⨾ mor F) e 𝒞.⨾ mor F ps ≡⟨" definition of functional composition 𝒮ℯ𝓉 "⟩′ mor F (edge ι e) 𝒞.⨾ mor F ps ≡⟨ ≡sym (Functor.comp F) { i.e., functors preserve composition } ⟩ mor F (edge ι e ++ ps) ≡⟨" definition of embedding and concatenation "⟩′ mor F (x ⟶[ e ]⟶ ps) ∎
Challenge: Define graphmap equality ‘≈g’ by extensionality –two graph maps are equal iff their vertex and edge maps are extensionally equal. This is far more relaxed than using propositional equality ‘≡’. Now show the stronger uniqueness claim:
∀{f : G ⟶ 𝒰₀ 𝒞} {F : 𝒫₀ G ⟶ 𝒞} → f ≈g (ι ⨾ 𝒰₁ F) → lift f ≡ F
However, saying each graphmap gives rise to exactly one unique functor is tantamount to
saying the type GraphMap G (𝒰₀ 𝒞)
is isomorphic to Functor (𝒫₀ G) 𝒞
, that is
(𝒫₀ G ⟶ 𝒞) ≅ (G ⟶ 𝒰₀ 𝒞)
—observe that this says we can ‘move’ 𝒫₀
from the left to
the right of an arrow at the cost of it (and the arrow) changing.
A few healthy exercises,
lift˘ : Functor 𝒫G 𝒞 → GraphMap G (𝒰₀ 𝒞) lift˘ F = ι ⨾g 𝒰₁ F  i.e., record {ver = obj F , edge = mor F ∘ edge ι} rid : ∀{f : GraphMap G (𝒰₀ 𝒞)} → ∀{x y} {e : x ⟶g y} → lift˘ (lift f) ≡ f rid = {! exercise !} lid : ∀{F : Functor 𝒫G 𝒞} → lift (lift˘ F) ≡ F lid = {! exercise !}
One can of course obtain these proofs from the other ones without recourse to definitions, however for comprehension one would do well to prove them from first principles. The worked out solutions are available in the literate source file of this document.
We can then provide an alternative, and more succinct, proof of uniqueness for ‘basis preservation’:
uniqueness’ : ∀{f h} → f ≡ (ι 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ h) → lift f ≡ h uniqueness’ {f} {h} f≈ι⨾𝒰₁h = begin lift f ≡⟨ ≡cong lift f≈ι⨾𝒰₁h ⟩ lift (ι 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ h) ≡⟨" definition of lift˘ "⟩′ lift (lift˘ h) ≡⟨ lid ⟩ h ∎
The difference between this proof and the original one is akin to the difference between heaven and earth! That or it's much more elegant ;)
𝒫 ⊣ 𝒰
Thus far, we have essentially shown \[(𝒫₀\, G \,⟶\, 𝒞) \quad≅\quad (G \,⟶\, 𝒰₀\, 𝒞)\] We did so by finding a pair of inverse maps:
lift : ( G ⟶ 𝒰₀ 𝒞) → (𝒫₀ G ⟶ 𝒞) lift˘ : (𝒫₀ G ⟶ 𝒞) → ( G ⟶ 𝒰₀ 𝒞)
This is nearly 𝒫 ⊣ 𝒰
which implies 𝒫
is a ‘freefunctor’ since it is leftadjoint to a forgetfulfunctor.
‘Nearly’ since we need to exhibit naturality:
For every graph map g
and functors F, k
we have
lift˘ (𝒫 g ⨾ k ⨾ F) ≈ g ⨾ lift˘ k ⨾ 𝒰 F
in the category of graphs.
Fokkinga (Theorem A.4), among others, would call these laws ‘fusion’
instead since they inform us how to compose, or ‘fuse’, a morphism with a
lift˘
ed morphism: Taking F
to be the identity and remembering that functors preserve
identities, we have that g ⨾ lift˘ K ≡ lift˘( 𝒫₁ g ⨾ K)
–we can push a morphism into a lift˘
at the cost of introducing a 𝒫₁
; dually for lift
ed morphisms.
First the setup,
module _ {G H : Graph} {𝒞 𝒟 : Category {ℓ₀} {ℓ₀}} (g : GraphMap G H) (F : Functor 𝒞 𝒟) where private lift˘ = λ {A} {C} B → freedom.lift˘ A {C} B lift = λ {A} {C} B → freedom.lift A {C} B open Category ⦃...⦄ module 𝒞 = Category 𝒞 module 𝒟 = Category 𝒟 module 𝒢𝓇𝒶𝓅𝒽 = Category 𝒢𝓇𝒶𝓅𝒽 module 𝒞𝒶𝓉 = Category (𝒞𝒶𝓉 {ℓ₀} {ℓ₀}) module 𝒮ℯ𝓉 = Category (𝒮e𝓉 {ℓ₀})
Just as we needed to prove two inverse laws for lift
and lift˘
,
we need two naturality proofs.
naturality˘ : ∀ {K : Functor (𝒫₀ H) 𝒞} → lift˘ (𝒫₁ g 𝒞𝒶𝓉.⨾ K 𝒞𝒶𝓉.⨾ F) ≡ (g 𝒢𝓇𝒶𝓅𝒽.⨾ lift˘ K 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ F) naturality˘ = graphmapext ≡refl ≡refl
That was easier than assumed!
Hahaha: Hard to formalise but so easy to prove lolz!
It says we can ‘shunt’ lift˘
into certain compositions at the cost
of replacing functor instances.
Now for the other proof:
naturality : ∀ {k : GraphMap H (𝒰₀ 𝒞)} → lift (g 𝒢𝓇𝒶𝓅𝒽.⨾ k 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ F) ≡ (𝒫₁ g 𝒞𝒶𝓉.⨾ lift k 𝒞𝒶𝓉.⨾ F) naturality {k} = funcext ≡refl (λ {x y p} → proof {x} {y} {p}) where open TypedPaths ⦃...⦄ instance G′ : Graph ; G′ = G H′ : Graph ; H′ = H proof : ∀ {x y : Graph.V G} {p : x ⇝ y} → mor (𝒫₁ g 𝒞𝒶𝓉.⨾ lift {H} {𝒞} k 𝒞𝒶𝓉.⨾ F) p ≡ mor (lift {G} {𝒟} (g 𝒢𝓇𝒶𝓅𝒽.⨾ k 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ F)) p proof {p = _ !} = functor (𝒫₁ g 𝒞𝒶𝓉.⨾ lift {H} {𝒞} k 𝒞𝒶𝓉.⨾ F) preservesidentities proof {p = x ⟶[ e ]⟶ ps} = begin mor (𝒫₁ g 𝒞𝒶𝓉.⨾ lift {H} {𝒞} k 𝒞𝒶𝓉.⨾ F) (x ⟶[ e ]⟶ ps) ≡⟨" By definition, “mor” distributes over composition "⟩′ (mor (𝒫₁ g) 𝒮ℯ𝓉.⨾ mor (lift {H} {𝒞} k) 𝒮ℯ𝓉.⨾ mor F) (x ⟶[ e ]⟶ ps) ≡⟨" Definitions of function composition and “𝒫₁ ⨾ mor” "⟩′ mor F (mor (lift {H} {𝒞} k) (mor (𝒫₁ g) (x ⟶[ e ]⟶ ps)))  This explicit path is in G ≡⟨" Lifting graphmap “g” onto a path "⟩′ mor F (mor (lift {H} {𝒞} k) (ver g x ⟶[ edge g e ]⟶ mor (𝒫₁ g) ps))  This explicit path is in H ≡⟨" Definition of “lift ⨾ mor” on inductive case for paths "⟩′ mor F (edge k (edge g e) 𝒞.⨾ mor (lift {H} {𝒞} k) (mor (𝒫₁ g) ps)) ≡⟨ functor F preservescomposition ⟩ mor F (edge k (edge g e)) 𝒟.⨾ mor F (mor (lift {H} {𝒞} k) (mor (𝒫₁ g) ps)) ≡⟨" Definition of function composition, for top part "⟩′ (edge g 𝒮ℯ𝓉.⨾ edge k 𝒮ℯ𝓉.⨾ mor F) e  ≈ mor F ∘ edge k ∘ edge g 𝒟.⨾ (mor (𝒫₁ g) 𝒮ℯ𝓉.⨾ mor (lift {H} {𝒞} k) 𝒮ℯ𝓉.⨾ mor F) ps ≡⟨" “𝒰₁ ⨾ edge = mor” and “edge” and “mor” are functorial by definition "⟩′ edge (g 𝒢𝓇𝒶𝓅𝒽.⨾ k 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ F) e 𝒟.⨾ mor (𝒫₁ g 𝒞𝒶𝓉.⨾ lift {H} {𝒞} k 𝒞𝒶𝓉.⨾ F) ps ≡⟨ { Inductive Hypothesis: } ≡cong₂ 𝒟._⨾_ ≡refl (proof {p = ps}) ⟩ edge (g 𝒢𝓇𝒶𝓅𝒽.⨾ k 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ F) e 𝒟.⨾ mor (lift {G} {𝒟} (g 𝒢𝓇𝒶𝓅𝒽.⨾ k 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ F)) ps ≡⟨" Definition of “lift ⨾ mor” on inductive case for paths "⟩′ mor (lift {G} {𝒟} (g 𝒢𝓇𝒶𝓅𝒽.⨾ k 𝒢𝓇𝒶𝓅𝒽.⨾ 𝒰₁ F)) (x ⟶[ e ]⟶ ps) ∎
Formally, we now have an adjunction:
𝒫⊣𝒰 : 𝒫 ⊣ 𝒰 𝒫⊣𝒰 = record{ ⌊_⌋ = lift˘ ; ⌈_⌉ = lift ; lid = lid ; rid = λ {G 𝒞 c} → rid {G} {𝒞} {c} ; lfusion = λ {G H 𝒞 𝒟 f F K} → naturality˘ {G} {H} {𝒞} {𝒟} f K {F} ; rfusion = λ {G H 𝒞 𝒟 f k F} → naturality {G} {H} {𝒞} {𝒟} f F {k} } where module _ {G : Graph} {𝒞 : Category} where open freedom G {𝒞} public
Observe that for the freedom proof we recalled
that ists determine a form of quantification, ‘folding’:
given an operation ⊕, we may form the operation [x₁, …, xₖ] ↦ x₁ ⊕ ⋯ ⊕ xₖ
.
Then used that to define our operation lift
, whose core was essentially,
module folding (G : Graph) where open TypedPaths G open Graph G  Given: fold : {X : Set} (v : V → X)  realise G's vertices as X elements (f : ∀ x {y} (e : x ⟶ y) → X → X)  realise paths as X elements → (∀ {a b} → a ⇝ b → X)  Then: Any path is an X value fold v f (b !) = v b fold v f (x ⟶[ e ]⟶ ps) = f x e (fold v f ps)
For example, what is the length of a path?
length : ∀{x y} → x ⇝ y → ℕ length = fold (λ _ → 0)  single walks are length 0. (λ _ _ n → 1 + n)  edges are one more than the  length of the remaining walk
Let’s verify that this is actually what we intend by the length of a path.
length! : ∀{x} → length (x !) ≡ 0 length! = ≡refl  True by definition of “length”: The first argument to the “fold” lengthind : ∀ {x y ω} {e : x ⟶ y} {ps : y ⇝ ω} → length (x ⟶[ e ]⟶ ps) ≡ 1 + length ps lengthind = ≡refl  True by definition of “length”: The secondargument to the “fold”
Generalising on length
, suppose we have a ‘cost function’ c
that assigns a cost of traversing
an edge. Then we can ask what is the total cost of a path:
pathcost : (c : ∀{x y}(e : x ⟶ y) → ℕ) → ∀{x y}(ps : x ⇝ y) → ℕ pathcost c = fold (λ _ → 0)  No cost on an empty path. (λ x e n → c e + n)  Cost of current edge plus  cost of remainder of path.
Now, we have length = pathcost (λ _ → 1)
: Length is just assigning a cost of 1 to each edge.
Under suitable conditions, list fold distributes over list catenation, can we find an analogue for paths? Yes. Yes, we can:
fold++ : ∀{X : Set} {v : V → X} {g : ∀ x {y} (e : x ⟶ y) → X} → (_⊕_ : X → X → X) → ∀{x y z : V} {p : x ⇝ y} {q : y ⇝ z} → (unitl : ∀{x y} → y ≡ v x ⊕ y)  Image of ‘v’ is left unit of ⊕ → (assoc : ∀ {x y z} → x ⊕ (y ⊕ z) ≡ (x ⊕ y) ⊕ z )  ⊕ is associative → let f : ∀ x {y} (e : x ⟶ y) → X → X f = λ x e ps → g x e ⊕ ps in fold v f (p ++ q) ≡ fold v f p ⊕ fold v f q fold++ {g = g} _⊕_ {x = x} {p = .x !} unitl assoc = unitl fold++ {g = g} _⊕_ {x = x} {p = .x ⟶[ e ]⟶ ps} unitl assoc = ≡cong (λ exp → g x e ⊕ exp) (fold++ _⊕_ {p = ps} unitl assoc) ⟨≡≡⟩ assoc
Compare this with the proofobligation of lift
.
We called our path catenation _++_
, why the same symbol as that for
list catenation?
How do we interpret a list over \(A\) as a graph? Well the vertices can be any element of \(A\) and an edge \(x ⟶ y\) merely indicates that ‘‘the item after \(x\) in the list is the element \(y\)’’, so we want it to be always true; or always inhabited without distinction of the inhabitant: So we might as well use a unit type.
module lists (A : Set) where open import Data.Unit listGraph : Graph listGraph = record { V = A ; _⟶_ = λ a a’ → ⊤ }
I haven’t a clue if this works, you read my reasoning above.
The only thing we can do is test our hypothesis by looking at the
typed paths over this graph. In particular, we attempt to show every
nonempty list of \(A\)’s corresponds to a path. Since a typed path needs
a priori the start and end vertes, let us construe
List A ≅ Σ n ∶ ℕ • Fin n → A
–later note that Path G ≅ Σ n ∶ ℕ • [n] 𝒢⟶ G
.
open TypedPaths listGraph open folding listGraph  Every nonempty list [x₀, …, xₖ] of A’s corresonds to a path x₀ ⇝ xₖ. toPath : ∀{n} (list : Fin (suc n) → A) → list fzero ⇝ list (fromℕ n) toPath {zero} list = list fzero ! toPath {suc n} list = list fzero ⟶[ tt ]⟶ toPath {n} (λ i → list(fsuc i))  Note that in the inductive case, “list : Fin (suc (suc n)) → A”  whereas “suc ⨾ list : Fin (suc n) → A”.   For example, if “list ≈ [x , y , z]” yields  “fsuc ⨾ list ≈ [y , z ]” and  “fsuc ⨾ fsuc ⨾ list ≈ [z]”.
Hm! Look at that, first guess and it worked! Sweet.
Now let’s realize the list fold as an instance of path fold,
 List type former List = λ (X : Set) → Σ n ∶ ℕ • (Fin n → X)  Usual list folding, but it's in terms of path folding. foldr : ∀{B : Set} (f : A → B → B) (e : B) → List A → B foldr f e (zero , l) = e foldr f e (suc n , l) = fold (λ a → f a e) (λ a _ rem → f a rem) (toPath l)  example listLength : List A → ℕ  result should clearly be “proj₁” of the list, anyhow: listLength = foldr (λ a rem → 1 + rem)  Nonempty list has length 1 more than the remainder. 0  Empty list has length 0.
Let’s prepare for a more useful example
 Empty list [] : ∀{X : Set} → List X [] = 0 , λ ()  Cons for lists _∷_ : ∀{X : Set} → X → List X → List X _∷_ {X} x (n , l) = 1 + n , cons x l where  “cons a l ≈ λ i : Fin (1 + n) → if i ≈ 0 then a else l i” cons : ∀{n} → X → (Fin n → X) → (Fin (suc n) → X) cons x l fzero = x cons x l (fsuc i) = l i map : ∀ {B} (f : A → B) → List A → List B map f = foldr (λ a rem → f a ∷ rem) []  looks like the usual map don’t it ;)  list concatenation _++ℓ_ : List A → List A → List A l ++ℓ r = foldr _∷_ r l  fold over ‘l’ by consing its elements infront of ‘r’  Exercise: Write path catenation as a pathfold.
These few adventures would suggest that much of list theory can be brought over to the world of paths. It looks promising, let me know dear reader if you make progress on related explorations!
This note took longer to write than I had initally assumed; perhaps I should have taken into account
It always takes longer than you expect, even when you take into account Hofstadter’s Law.
Lessons learned:
The astute reader may have noticed that the tone of writing sometimes changes drastically. This is because some of this article was written by me in March 2016 and I wished to preserve interesting writing style I then had –if anything to contrast with my now somewhat semiformal style.
This article was motivated while I was reading Conceptual Mathematics for fun. One of the problems was to show that paths over a graph form a category and do so freely. It took me about 20 minutes on paper and pencil, but this resulting mechanisation took much more time –but it was also much more fun!
I had fun writing this up & I hope you enjoy it too :)
( This article is not yet ‘done’, but good enough for now. )
We attempt to motivate the structure of a Heyting Algebra by considering ‘inverse problems’.
For example,
What if we decided, for security, to change our protocol from using addition to using minimum. That is, we encode our message \(m\) as \(z = x ↓ m\). Since minimum is not invertible, we decide to send our encoded messages with a ‘context’ \(c\) as a pair \((z, c)\). From this pair, a unique number \(m′\) can be extracted, which is not necessarily the original \(m\). Read on, and perhaps you'll figure out which messages can be communicated 😉
This exploration demonstrates that relative pseudocomplements
In some sense, the pseudocomplement is the “best approximate inverse” to forming meets, minima, intersections.
Along the way we develop a number of the theorems describing the relationships between different structural components of Heyting Algebras; most notably the internalisation of much of its own structure.
The article aims to be selfcontained, however it may be helpful to look at this lattice cheat sheet (•̀ᴗ•́)و
( Photo by Ludovic Fremondiere on Unsplash )
Recall that an order is nothing more than a type \(Carrier\) with a relation \(\_⊑\_\) such that the following list of axioms holds: For any \(x,y,z : Carrier\),
\[\eqn{⊑reflexive}{x ⊑ x}\]
\[\eqn{⊑transitive}{x ⊑ y \landS y ⊑ z \impliesS x ⊑ z}\]
\[\eqn{⊑antisymmetric}{x ⊑ y \landS y ⊑ x \impliesS x = y}\]
This models notions of containment, inclusion, hierarchy, and approximation.
Recall that a meet is an additional operation \(\_⊓\_\) specified by \[\eqnColour{Meet Characterisation}{ z \;⊑\; x ⊓ y \equivS z ⊑ x \landS z ⊑ y}{green}\]
Meets model notions of greatest lower bounds, or infima.
Observe that meets allow us to encode a pair of inclusions as a single inclusion! That is, an external conjunction \(∧\) is equivalent to an internal meet \(⊓\).
In computing parlance, think of a toplevel ‘method’ that takes and yields data. In many languages, the method itself can be thought of as data! It is this kind of externalinternal duality we are alluding to –and is captured nicely by exponential objects in category theory, of which we are considering a special case.
However, we cannot define complement in the same fashion, since the resulting relation is not over the resulting vertex set!
Indeed suppose we define \(∼ (V′, E′) \;:=\; (V  V′, E  E′)\), then the discrete graph \((V, ø)\) has complement \((ø, E)\) which is a ‘graph’ with many edges \(E\) but no vertices! —Provided \((V,E)\) has edges.
However, the edges can be relativised to the vertices to produce the pseudocomplement.
It is not clear how \(\ref{Meet Characterisation}\) can be used to prove properties about meet.
The laws \ref{⊑antisymmetric} and \ref{⊑reflexive} can be fused together into one law: \[\eqnColour{Indirect Equality}{ x = y \equivS \left(∀ z \;•\quad ⊑\; z ⊑ x \equivs z ⊑ y\right)}{green}\]
That is, for all practical purposes, \(x = y\) precisely when they have the same subparts!
Using this, we can prove that meet is idempotent \(x ⊓ x = x\), symmetric \(x ⊓ y = y ⊓ x\), associative \(x ⊓ (y ⊓ z) = (x ⊓ y) ⊓ z\), and monotonic: \(a ⊑ b \landS c ⊑ d \impliesS a ⊓ c \sqleqS b ⊓ c\).
Below we use this principle in a number of places, for example \ref{Distributivity of → over ⊓}.
The relative pseudocomplement of \(a\) wrt \(b\), \((a ⟶ b)\), is “the largest element \(x\) that ensures modus ponens”, i.e., “the largest element \(x\) whose meet with \(a\) is contained in \(b\)”:
\[\eqnColour{Relative PseudoComplement Characterisation}{\hspace{5em} a ⊓ x \;⊑\; b \equivS x \;⊑\; (a → b)}{green}\]
This is also sometimes denoted \(a ➩ b\) or \(a \backslash b\).
In five of the above settings this becomes,
\(m ↓ x ≤ n \equivS x ≤ (m → n)\)
Not at all clear what to do so looking for a counterexample shows that pseduocomplements cannot exist for the naturals otherwise selecting \(m ≔ n\) yields:
\begin{calc} ∀ x \;•\quad n ↓ x \;≤\; n \equivS x \;≤\; (n → n) \step{ Weakening: The minimum is at least both its arguments } ∀ x \;•\quad \mathsf{true} \equivS x \;≤\; (n → n) \step{ Identity of ≡ } ∀ x \;•\quad x \;≤\; (n → n) \step{ Definition of Infinity } (n → n) \;=\; +∞ \end{calc}Thus the existence of psquedocomplements implies the existence of a top element “\((n ⟶ n) = +∞\)”!
Okay, no problem: Let's consider 𝒩, the set of natural numbers along with a new maximum element “+∞”; then we can define a relative pseudocomplement. \[\eqn{Definition of → for 𝒩}{m → n \quad=\quad \mathsf{if}\; m > n \;\mathsf{then}\; n \;\mathsf{else}\; +∞ \;\mathsf{fi}}\] We now have a way to approximate an inverse to minima, which is in general not invertible.
–This definition works for any linear order with a top element—
\(p ∧ x ⇒ q \equivS x ⇒ (p → q)\)
Starting with the right side,
\begin{calc} x \impliesS (p → q) \step{ Characterisation of pseudocomplement } p ∧ x \impliesS q \step{ Symmetry of ∧ } x ∧ p \impliesS q \step{ Shunting } x \impliesS (p ⇒ q) \end{calc}Hence, by indirect equality, \(p → q \;=\; p ⇒ q \;=\; ¬ p ∨ q\).
\((A ∩ X) \;⊆\; B \equivS X \;⊆\; (A → B)\)
Where we can similarly verify \((A → B) \;\;=\;\; ∼ A ∪ B\).
It is interesting to note that \(x ∈ (A → B) \equivS x ∈ A \impliess x ∈ B\).
\(G ⊓ X \;⊑\; G′ \equivS X \;⊑\; (G → G′)\)
We disclosed earlier that subgraph difference did not yield valid subgraphs, but if we relativised the resulting edge relationship to only consider the resulting vertex set, then we do have a subgraph.
That is, subgraphs admit relative pseduocomplements, by \((V₁, E₁) → (V₂, E₂) \equivS (V₁  V₂, (E₁  E₂) ∩ (V₃ × V₃))\) where \(V₃ = V₁  V₂\).
The result of \(G₁ → G₂\) is the largest subgraph of \(G₁\) that does not overlap with \(G₂\).
\((A × B → C) \equivS A → (B → C)\)
In the setting of functions, this says that a function taking a pair of inputs can be considered as a function that takes one input, of type \(A\), then yields another function that takes the second input. That is, the two inputs no longer need to be provided at the same time. This is known as currying.
\(P, Q ⊢ R \equivS P ⊢ (Q → R)\)
In the logical formulae setting, the characterisation is known as the deduction theorem and allows us to ‘suppose’ an antecedent in the process of a proof.
In the above we have witnessed that the usual naturals admit pseudocomplements precisely when infinity is considered a number, that it is exactly implication for the Booleans, that it internalises implication for sets, and for subgraphs it is encodes the largest complementary subgraph.
In some sense, the pseudocomplement is the “best approximate inverse” to forming minima.
We leave the remaining example as an exercise 😉
Recall the \ref{Relative PseudoComplement Characterisation} says \[a ⊓ x \;⊑\; b \equivS x \;⊑\; (a → b)\]
We readily obtain some results by making parts of the characterisation true. E.g., making the left/right part true by instantiating the variables.
For example, taking \(x ≔ (a → b)\) yields \[\eqn{Modus Ponens}{a ⊓ (a → b) \quad⊑\quad b}\] Exercise: Prove this directly!
Using the principle of indirect equality, we can strengthen this into an equality and also obtain a close variant. \[\eqn{Strong Modus Ponens}{a ⊓ (a → b) \quad=\quad a ⊓ b}\]
\[\eqn{Absorption}{b ⊓ (a → b) \quad=\quad b}\]
\ref{Modus Ponens} suggest an order preservation property:
\begin{calc} a → x \quad⊑\quad a → y \step{ \ref{Relative PseudoComplement Characterisation} } a ⊓ (a → x) \quad⊑\quad y \stepWith{\Leftarrow}{ \ref{⊑transitive} } a ⊓ (a → x) \quad⊑\quad x \landS x \quad⊑\quad y \step{ \ref{Modus Ponens} and Identity of ∧ } x \quad⊑\quad y \end{calc}Hence we have derived the following consequent weakening rule, \[\eqn{Monotonicity₂}{a → x \quad⊑\quad a → y \qquad\Leftarrow\qquad x \;⊑\; y}\]
An immediate sequel is, \[\eqn{Weakening₂}{a → (x ⊓ y) \quad⊑\quad a → y}\]
Here are a few more properties for you to get familiar with this, the first three are immediate instantiation of the characterisation, while the fourth one may require using monotonicity properties of meet, and the final one uses indirect equality.
\[\eqn{Top Element}{x \quad⊑\quad a → a}\]
\[\eqn{Strengthening}{b \quad⊑\quad a → b}\]
\[\eqn{UnCurrying}{x \quad⊑\quad a → a ⊓ x}\]
\[\eqn{Weakening}{x \quad⊑\quad (a ⊓ b) → b}\]
\[\eqn{Antitonicity₁}{a → x \quad⊑\quad b → x \qquad\Leftarrow\qquad b \;⊑\; a}\]
\[\eqn{SelfSubDistributive}{a → (b → c) \sqleqS (a → b) → (a → c)}\]
Observe that, in the functional case, \ref{UnCurrying} says we have a function \[X → (A → (A × X)) \;\;:\;\; x \mapsto (a \mapsto (a, x))\]
Recall \ref{Top Element} said that we have a greatest element in the order. Suppose our universe, \(Carrier\), is nonempty and has some element \(c₀\). Then we may define \(⊤ = (c₀ → c₀)\). This element is infact invariant over \(c₀\):
\[\eqn{Top Element Invariance}{ ∀ x \;•\quad ⊤ \quad=\quad (x → x) }\] The proof is simply an appeal to \ref{⊑antisymmetric} then \ref{Top Element} twice.
From this, we obtain two immediate properties about meets.
\[\eqn{Identity of ⊓}{ x ⊓ ⊤ \quad=\quad x }\]
\[\eqn{Shunting}{x \;⊑\; a → (b → c) \equivS x \;⊑\; (a ⊓ b) → c}\]
Along with two properties about top element.
\[\eqn{Right Zero of →}{ x → ⊤ \quad=\quad ⊤ }\]
\[\eqn{Left Identity of →}{ ⊤ → x \quad=\quad x }\]
Notice that \ref{Right Zero of →} internalises what it means to be a \ref{Top Element}; namely \(∀ x \;•\; x ≤ ⊤\).
A bit more interestingly, we can fuse a meet with a pseudocomplement:
\[\eqn{Submutual Associtivity of ⊓ and →}{\hspace{3em}x ⊓ (a → b) \sqleqS (x ⊓ a) → b}\]
The converse of this statement is not true in general. In particular, in the presence of bottoms, the converse, \((x ⊓ a) → b \;\;⊑\;\; x ⊓ (a → b)\), implies that there is only one possible value: ⊥!
Exercise: Show that the converse statement implies \(⊥ = ⊤\) then from this conclude that \(x = ⊥\), for all \(x\).
We have seen how relative pseudocomplements allowed us to reflect external characterisations such as \ref{Top Element} that use logical connectives into internal forms such as \ref{Right Zero of →} which only uses the symbols of the language, namely →, ⊓, ⊤.
Even the order, which takes two elements and yields a Boolean, can be internalised: \[\eqn{Internalising}{a ⊑ b \equivS (a → b) = ⊤}\]
Notice the striking resemblance to the \ref{Definition of → for 𝒩}!
\ref{Meet Characterisation} can also be internalised:
\begin{calc} x \sqleqS (a → c) \;⊓\; (a → b) \step{ \ref{Meet Characterisation} } x ⊑ (a → c) \landS x ⊑ (a → b) \step{ \ref{Relative PseudoComplement Characterisation} } a ⊓ x ⊑ c \landS a ⊓ x ⊑ b \step{ \ref{Meet Characterisation} } a ⊓ x \sqleqS c ⊓ b \step{ \ref{Relative PseudoComplement Characterisation} } x \sqleqS a → (c ⊓ b) \end{calc}Hence, by the principle of \ref{Indirect Equality}, we have \[\eqn{Distributivity of → over ⊓}{ a → (c ⊓ b) \quad=\quad (a → c) \;⊓\; (a → b) }\]
Recall \ref{Meet Characterisation} says \[ z \;⊑\; x ⊓ y \equivS z ⊑ x \landS z ⊑ y \] If we now mirror ‘⊑’ with ‘⊒’ we obtain –after renaming ⊓ with ⊔– \[ z \;⊒\; x ⊔ y \equivS z ⊒ x \landS z ⊒ y \] That is, we obtain upper bounds! \[\eqnColour{Join Characterisation}{ x ⊔ y \;⊑\; z \equivS x ⊑ z \landS y ⊑ z}{green}\]
Moreover, using →, it can be shown that joins and meets distribute over each other.
\[\eqn{Distributivity₁}{a \;⊔\; (b ⊓ c) \quad=\quad (a ⊔ b) \;⊓\; (a ⊔ c)}\] \[\eqn{Distributivity₂}{a \;⊓\; (b ⊔ c) \quad=\quad (a ⊓ b) \;⊔\; (a ⊓ c)}\]
Can we obtain the usual De Morgan's law?
Suppose we have a least element \(⊥ : Carrier\), i.e., for any \(x\), \[\eqn{Bottom Element}{ ⊥ \sqleqs x}\]
For usual ℕ, 𝔹, sets, and graphs this amounts to 0, \(\mathsf{false}\), \(\emptyset\), and the empty graph with no nodes nor any edges, respectively.
Unsurprising this can also be internalised! \[\eqn{ex falso quod libet}{⊥ → a \quad=\quad ⊤}\]
Moreover we can now define an operation \(¬\_\) as follows, \[\eqn{PseudoComplement Definition}{ ¬ x \quad=\quad (x → ⊥) }\]
A complement of an element \(x\) is an element \(y\) that is disjoint from \(x\) and together their join is the top element.
A pseudocomplement generalises this idea by discarding the second half of that conjunction; i.e., instead requiring a greatest element \(y\) disjoint from \(x\).
Indeed this is the case here,
\begin{calc} x \;⊓\; ¬ x \quad=\quad ⊥ \step{ \ref{⊑antisymmetric} } (x \;⊓\; ¬ x) \sqleqs ⊥ \landS ⊥ \sqleqs (x \;⊓\; ¬ x) \step{ \ref{Bottom Element} } x \;⊓\; ¬ x \sqleqS ⊥ \step{ \ref{Modus Ponens} } \mathsf{true} \end{calc}We now have the linguistic prerequisites to actually express De Morgan's laws. \[\eqn{Constructive De Morgan}{¬(x \;⊔\; y) \quad=\quad ¬ x \;⊓\; ¬ y}\]
The other form \(¬(x ⊓ y) \;=\; ¬ x ⊔ ¬ y\) however is not true in general. Likewise neither are double negation, \(¬¬x \;=\; x\), nor the law of the the excluded middle \(x \;⊔\; ¬ x \;=\; ⊤\).
Indeed for 𝒩, the natural numbers extended with +∞, the law of the exluded middle is falsified as follows:
\begin{calc} 3 ↑ ¬ 3 \step{ \ref{PseudoComplement Definition} } 3 ↑ (3 → 0) \step{ \ref{Definition of → for 𝒩} } 3 ↑ 0 \step{ Maximum } 3 \end{calc}Likewise double negation is falsified by \(¬ ¬ 3 \;=\; 0\).
Exercise: Find an example to falsify the other De Morgan's law.
Having ⊓, ⊔, ⊥, ⊤, → together constitutes a Heyting Algebra.
It took a while, but we've more or less shimmied our way to the structure needed for Heyting Algebras.
Hope you had fun!
How my blog is setup (•̀ᴗ•́)و
Here are some notable features of my blog.
Orgmode is an outliner, a rich markup language, spreadsheet tool, literate programming systems, and so much more.
Orgmode syntax is very natural; e.g., the following is Orgmode!
+ Numbered and bulleted lists are as expected.  Do the things: 1. This first 2. This second 44. [@44] This fortyfourth  [@𝓃] at the beginning of an iterm forces list numbering to start at 𝓃  [ ] or [X] at the beginning for checkbox lists  Use Alt ↑, ↓ to move items up and down lists; renumbering happens automatically. + Definitions lists:  term :: def + Use a comment, such as # separator, between two lists to communicate that these are two lists that happen to be one after the other. Or use any nonindented text to split a list into two. * My top heading, section words ** Child heading, subsection more words *** Grandchild heading, subsubsection even more!
You make a heading by writing * heading
at the start of a line, then you can
TAB to fold/unfold its contents. A table of contents, figures, tables can be
requested as follows:
# figures not implemented in the HTML backend # The 𝓃 is optional and denotes headline depth #+toc: headlines 𝓃 #+toc: figures #+toc: tables
Markup elements can be nested.
Syntax  Result 

/Emphasise/ , italics 
Emphasise 
*Strong* , bold 
Strong 
*/very strongly/* , bold italics 
very strongly 
=verbatim= , monospaced typewriter 
verbatim 
+deleted+ 

_inserted_ 
inserted 
super^{script}ed 
super^{script}ed 
sub_{scripted}ed 
sub_{scripted}ed 
\\
to force line breaks without starting a new paragraph
, to form a horizontal ruleExport In Emacs, press Cc Ce h o to obtain an HTML format of the Orgmode markup; use Cc Ce l o to obtain a PDF rendition.
Working with tables
#+ATTR_HTML: :width 100% #+name: mytbl #+caption: Example table  Who?  What?  +  me  Emacs   you  Org 
Note the horizontal rule makes a header row and is formed by typing   then pressing TAB . You can TAB between cells.
Working with links
Link syntax is [[source url][description]]
; e.g., we can refer to the above
table with [[mytbl][woah]]
.
Likewise for images: file:pathtoimage.
Source code
1: #+begin_src C 2: int tot = 1; (start) 3: for (int i = 0; i != 10; i++) (loop) 4: tot *= i; (next) 5: printf("The factorial of 10 is %d", tot); 6: #+end_src
The labels (ref:name)
refer to the lines in the source code and can be
referenced with link syntax: [[(name)]]
. Hovering over the link, in the HTML
export, will dynamically highlight the corresponding line of code. To stripout
the labels from the displayed block, use r n
in the header so it becomes
#+begin_src C r n
, now the references become line numbers.
Another reason to use Org:
If you use :results raw
, you obtain dynamic templates that may use Orgmarkup:
printf("*bold* +%d+ (strikethrough) /slanted/", 12345);
bold 12345 (strikethrough) slanted
bold 12345 (strikethrough) slanted
Also: Notice that a C program can be run without a main
;)
Mathematics
$\sin^2 x + \cos^2 x = \int_\pi^{\pi + 1} 1 dx = {3 \over 3}$
\[ ⇒ \quad \sin^2 x + \cos^2 x = \int_\pi^{\pi + 1} 1 dx = {3 \over 3} \]
$
, use \[...\]
to display a formula on its own line, centred.#+name: euler \begin{equation} e ^ {i \pi} + 1 = 0 \end{equation} See equation [[euler]].
⇒
\begin{equation} \label{orgbec0500} e ^ {i \pi} + 1 = 0 \end{equation}See equation \eqref{orgbec0500}.
Let's use orgstaticblock to make our blog. Why?
Let's declare the necessary basic facts of our blog.
1: (setq orgstaticblogpublishtitle "Life & Computing Science") 2: (setq orgstaticblogpublishurl "https://alhassy.github.io/") 3: (setq orgstaticblogpublishdirectory "~/blog/") 4: (setq orgstaticblogpostsdirectory "~/blog/posts/") 5: (setq orgstaticblogdraftsdirectory "~/blog/drafts/") 6: 7: ;; Use “#+filetags: τ₁ τ₂ … τₙ” 8: (setq orgstaticblogenabletags t) 9: 10: ;; I'd like to have tocs and numbered headings 11: (setq orgexportwithtoc t) 12: (setq orgexportwithsectionnumbers t)
What do we want to be inserted into the head of every page?
Firstly, we want some styling rules to be loaded.
1: (concat 2: "<meta name=\"author\" content=\"Musa Alhassy ??? \"> 3: <meta name=\"referrer\" content=\"noreferrer\">" 4: "<link href=\"usualorgfrontmatter.css\" rel=\"stylesheet\" type=\"text/css\" />" 5: "<link href=\"orgnotesstyle.css\" rel=\"stylesheet\" type=\"text/css\" />" 6: "<link href=\"floatingtoc.css\" rel=\"stylesheet\" type=\"text/css\" />" 8: "<link rel=\"icon\" href=\"images/favicon.png\">")
In addition, we have two more pieces we would like to add to the header: Support
for dynamic codeline highlighting, §3, and support for using
LaTeXstyle notation to write mathematics, §4. We will use a
nowebref named myhtmlheader
to refer to them, which are then catenated below.
(setq orgstaticblogpageheader (concat orghtmlheadextra ;; Alterd by ‘orgspecialblockextras’ <<myhtmlheader>> ))
[ Warning:
The nowebref invocation l <<𝓍𝓈>> r
expands into
l 𝓍₀ r l 𝓍₁ r ⋮ l 𝓍ₙ r
𝓍ᵢ
are the lines referenced by 𝓍𝓈
.
As such, we had our reference call, above, in its own line!
]
I want to have a nice banner at the top of every page, which should link to useful parts of my blog.
1: (setq orgstaticblogpagepreamble 2: "<div class=\"header\"> 3: <a href=\"https://alhassy.github.io/\" class=\"logo\">Life & Computing Science</a> 4: <br> 5: <a href=\"https://alhassy.github.io/AlBasmala\">AlBasmala</a> 6: <a href=\"https://alhassy.github.io/archive\">Archive</a> 7: <a href=\"https://alhassy.github.io/tags\">Tags</a> 8: <a href=\"https://alhassy.github.io/rss.xml\">RSS</a> 9: <a href=\"https://alhassy.github.io/about\">About</a> 10: </div>")
Note that we could have been needlessly more generic by using, say,
(orgstaticbloggetabsoluteurl orgstaticblogrssfile)
,
instead of hardcoding the links.
I want to style it as follows:
fantasy
font1: .header { 2: /* Try to load ‘fantasy’ if possible, else try to load the others. */ 3: fontfamily: fantasy, monospace, Times; 4: textalign: center; 5: overflow: hidden; 6: /* backgroundcolor: #f1f1f1 !important; */ 7: /* background: #4183c4 !important; */ 8: paddingtop: 10px; 9: paddingbottom: 10px; 10: boxshadow: 0 2px 10px 2px rgba(0, 0, 0, 0.2); 11: } 12: 13: .header a.logo { 14: fontsize: 50px; 15: fontweight: bold; 16: } 17: 18: .header a { 19: color: black; 20: padding: 12px; 21: textdecoration: none; 22: fontsize: 18px; 23: } 24: 25: .header a:hover { 26: backgroundcolor: #ddd; 27: backgroundcolor: #fff; 28: color: #4183c4; 29: }
Notice that as you hover over the references, such as this, the corresponding
line of code is highlighted! Within a src
block, one uses the switches n r
to enable references via line numbers, then declares (ref:name)
on line
and refers to it by [[(name)][description]]
. Orgmode by default styles
such highlighting.
"<script type=\"text/javascript\"> /* @licstart The following is the entire license notice for the JavaScript code in this tag. Copyright (C) 20122020 Free Software Foundation, Inc. The JavaScript code in this tag is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License (GNU GPL) as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The code is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. As additional permission under GNU GPL version 3 section 7, you may distribute nonsource (e.g., minimized or compacted) forms of that code without the copy of the GNU GPL normally required by section 4, provided you include this license notice and a URL through which recipients can access the Corresponding Source. @licend The above is the entire license notice for the JavaScript code in this tag. */ <!/*><![CDATA[/*><!*/ function CodeHighlightOn(elem, id) { var target = document.getElementById(id); if(null != target) { elem.cacheClassElem = elem.className; elem.cacheClassTarget = target.className; target.className = \"codehighlighted\"; elem.className = \"codehighlighted\"; } } function CodeHighlightOff(elem, id) { var target = document.getElementById(id); if(elem.cacheClassElem) elem.className = elem.cacheClassElem; if(elem.cacheClassTarget) target.className = elem.cacheClassTarget; } /*]]>*///> </script>"
[ Remark: TODO Before we move on, I'd like to have heavy red font for links.
a {color:#DD514C;textdecoration:none;fontweight:700}
Org loads the MathJax display engine for mathematics whenever users
write LaTeXstyle math delimited by $...$
or by \[...\]
. Here is an example.
This is the CSS that Org loads by default.
"<script type=\"text/xmathjaxconfig\"> MathJax.Hub.Config({ displayAlign: \"center\", displayIndent: \"0em\", \"HTMLCSS\": { scale: 100, linebreaks: { automatic: \"false\" }, webFont: \"TeX\" }, SVG: {scale: 100, linebreaks: { automatic: \"false\" }, font: \"TeX\"}, NativeMML: {scale: 100}, TeX: { equationNumbers: {autoNumber: \"AMS\"}, MultLineWidth: \"85%\", TagSide: \"right\", TagIndent: \".8em\" } }); </script> <script type=\"text/javascript\" src=\"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeXAMS_HTML\"></script> "
\[ p ⊓ q = p \quad ≡ \quad p ⊔ q = q \label{GoldenRule}\tag{GoldenRule}\]
Look at \ref{GoldenRule}, it says, when specialised to numbers, the minimum of two items is the first precisely when the maximum of the two is the second —d'uh!
[ Warning:
We can make an equation ℰ named 𝒩 and refer to it by ℒ by declaring
\[ℰ \tag{𝒩} \label{ℒ} \]
then refer to it with \ref{ℒ}
. However, if 𝒩 contains
Unicode, then the reference will not generally be ‘clickable’ —it wont take
you to the equation's declaration site. For example, \ref{⊑Definition} below
has Unicode in both its tag and label, and so clicking that link wont go
anywhere, whereas \ref{OrderDefinition} has Unicode only in its tag, with the
label being \label{OrderDefinition}
, and clicking it takes you to the formula.
\[ p ⊑ q \quad ≡ \quad p ⊓ q = p \tag{⊑Definition}\label{⊑Definition} \]
\[ p ⊑ q \quad ≡ \quad p ⊔ q = q \tag{⊑Definition}\label{OrderDefinition} \]
]
The following rule for anchors a {⋯}
resurrects \ref{}
calls via MathJax
—which orgnotesstyle
kills.
a { whitespace: pre !important; }
I would like to have a table of contents that floats so that it is accessible to the reader in case they want to jump elsewhere in the document quickly —possibly going to the top of the document.
When we write #+toc: headlines 2
in our Org, HTML export produces the following.
1: <div id="tableofcontents"> 2: <h2>Table of Contents</h2> 3: <div id="texttableofcontents"> 4: <ul> 5: <li> section 1 </li> 6: ⋮ 7: <li> section 𝓃 </li> 8: </ul> 9: </div> 10: </div>
Hence, we can style the table of contents by writing rules that target those
id
's. We use the following rules, adapted from the Worg community.
1: /*TOC inspired by https://orgmode.org/worg/ */ 2: #tableofcontents { 3: /* Place the toc in the top right corner */ 4: position: fixed; right: 0em; top: 0em; 5: margintop: 120px; /* offset from the top of the screen */ 6: 7: /* It shrinks and grows as necessary */ 8: padding: 0em !important; 9: width: auto !important; 10: minwidth: auto !important; 11: 12: fontsize: 10pt; 13: background: white; 14: lineheight: 12pt; 15: textalign: right; 16: 17: boxshadow: 0 0 1em #777777; 18: webkitboxshadow: 0 0 1em #777777; 19: mozboxshadow: 0 0 1em #777777; 20: webkitborderbottomleftradius: 5px; 21: mozborderradiusbottomleft: 5px; 22: 23: /* Ensure doesn't flow off the screen when expanded */ 24: maxheight: 80%; 25: overflow: auto;} 26: 27: /* How big is the text “Table of Contents” and space around it */ 28: #tableofcontents h2 { 29: fontsize: 13pt; 30: maxwidth: 9em; 31: border: 0; 32: fontweight: normal; 33: paddingleft: 0.5em; 34: paddingright: 0.5em; 35: paddingtop: 0.05em; 36: paddingbottom: 0.05em; } 37: 38: /* Intially have the TOC folded up; show it if the mouse hovers it */ 39: #tableofcontents #texttableofcontents { 40: display: none; 41: textalign: left; } 42: 43: #tableofcontents:hover #texttableofcontents { 44: display: block; 45: padding: 0.5em; 46: margintop: 1.5em; }
[ Strange: If I zoom in over 100% in my browser, the toc disappears until I zoom out. ]
Since the table of contents floats, the phrase Table of Contents is rather
‘in your face’, so let's use the more subtle Greek letter Ξ
.
1: (adviceadd 'orghtmltranslate :beforeuntil 'displaytocasΞ) 2: 3: ;; (adviceremove 'orghtmltranslate 'displaytocasΞ) 4: 5: (defun displaytocasΞ (phrase info) 6: (when (equal phrase "Table of Contents") 7: (scollapsewhitespace 8: "<a href=\"javascript:window.scrollTo(0,0)\" 9: style=\"color: black !important; borderbottom: none !important;\" 10: class=\"tooltip\" 11: title=\"Go to the top of the page\"> 12: Ξ 13: </a>")))
How did I get here?
Every article declaratively has an associated image ^_^
emacsbirthdaypresent.png
:)~/blog/images/
directory.1: (cldefun my/orgstaticblogassembleimage (file) 2: "Assemble the value of ‘#+fileimage: image width height border?’ as an HTML form." 3: (withtempbuffer 4: (insertfilecontents file) 5: (gotochar 0) 6: (searchforwardregexp "^\\#\\+fileimage: \\(.*\\)" nil t) 7: (let [(image width height noborder?) 8: (ssplit " " (substringnoproperties 9: (or (matchstring 1) 10: "emacsbirthdaypresent.png")))] 11: (setq width (or width 350)) 12: (setq height (or height 350)) 13: (setq noborder? (if noborder? "" "style=\"border: 2px solid black;\"")) 14: (format "<center> <img src=\"images/%s\" alt=\"Article image\" 15: %s width=\"%s\" height=\"%s\" align=\"top\" /> </center>" 16: image noborder? width height))))
To make use of orgnotesstyle
, I need the title to use the title
class
but orgstaticblog
uses the posttitle
blog, so I'll override the
orgstaticblog
preamble method to simply use an auxiliary div.
orgnotesstyle
has too much vertical space after the title,
let's reduce it so that the article's data can follow it smoothly.1: (defun orgstaticblogpostpreamble (postfilename) 2: "Returns the formatted date and headline of the post. 3: This function is called for every post and prepended to the post body. 4: Modify this function if you want to change a posts headline." 5: (concat 6: ;; The title 7: "<h1 class=\"posttitle\">" 8: "<div class=\"title\" style=\"margin: 0 0 0 0 !important;\">" 9: "<a href=\"" (orgstaticbloggetposturl postfilename) "\">" 10: (orgstaticbloggettitle postfilename) 11: "</a>" 12: "</h1></div>" 13: ;; Move to the footer? Near the ‘Tags’ of the article? 14: ;; The date 15: "<div style=\"textalign: center;\">" 16: (formattimestring (orgstaticbloggettext 'dateformat) 17: (orgstaticbloggetdate postfilename)) 18: "</div>" 19: ;; The article's image 20: (my/orgstaticblogassembleimage postfilename) 21: "<br><center><strong>Abstract</strong></center>"))
I'd like to be able to quickly change the blurb on the index page, so we make a variable for that —consisting of Orgmarkup.
1: (setq indexcontentheader 2: (concat 3: "Here are some of my latest thoughts..." 4: " badge:Made_withLisp such as doc:threadfirst and doc:loop (•̀ᴗ•́)و" 5: " tweet:https://alhassy.github.io/"))
The index page is a multipost page, so I'll override the
orgstaticblogassemblemultipostpage
method so that articles are
summarised by their title, data & image, ‘abstract’, and a readmore badge.
Abstract
, whose contents
are used as the preview of the article. See §11 for a template.1: (setq showreadingtime nil) 2: 3: (defun orgstaticblogassemblemultipostpage 4: (pubfilename postfilenames &optional frontmatter) 5: "Assemble a page that contains multiple posts one after another. 6: Posts are sorted in descending time." 7: (setq postfilenames 8: (sort postfilenames (lambda (x y) 9: (timelessp (orgstaticbloggetdate y) 10: (orgstaticbloggetdate x))))) 11: (withtempbuffer 12: (insert 13: (concat 14: "#+EXPORT_FILE_NAME: " pubfilename 15: "\n#+options: toc:nil title:nil htmlpostamble:nil" 16: "\n#+title: " (if (equal "index" (fbase pubfilename)) 17: orgstaticblogpublishtitle 18: (fbase pubfilename)) 19: "\n#+begin_export html\n " 20: orgstaticblogpagepreamble 21: orgstaticblogpageheader 22: (if frontmatter frontmatter "") 23: "\n#+end_export" 24: 25: "\n\n" 26: (if (equal "index" (fbase pubfilename)) 27: (format "#+begin_export html\n%s\n#+end_export\n%s" 28: orgstaticblogpageheader indexcontentheader) 29: "") 30: 31: "\n\n" ;; abstracts of posts 32: (threadlast postfilenames 33: (map 34: (format 35: (concat 36: ;; ⟨0⟩ Title and link to article 37: "#+HTML: <h2 class=\"title\"><a href=\"%s\"> %s</a></h2>" 38: ;; ⟨1⟩ Tags and reading time 39: "\n#+begin_center\n%s\n%s\n#+end_center" 40: ;; ⟨2⟩ Article image 41: "\n@@html:%s@@" 42: ;; ⟨3⟩ Preview 43: "\n#+INCLUDE: \"%s::*Abstract\" :onlycontents t" 44: ;; ⟨4⟩ “Read more” link 45: "\n@@html:<p style=\"textalign:right\">@@" 46: " badge:Readmoregreen%sreadthedocs @@html:</p>@@") 47: ;; ⟨0⟩ Title and link to article 48: (concat orgstaticblogpublishurl (fbase it)) 49: (orgstaticbloggettitle it) 50: ;; ⟨1⟩ Tags and reading time 51: (concat octoicon:tag " " 52: (sjoin " " 53: (map (format "badge:%sgrey%stag%s.html" 54: (sreplace "" "_" it) 55: orgstaticblogpublishurl it) 56: (orgstaticbloggettags it)))) 57: (if (not showreadingtime) 58: "" 59: (format "\n%s %s mins read" 60: octoicon:clock 61: (withtempbuffer (insertfilecontents it) 62: (orgasciiexportasascii) 63: (setq __x 64: (countwords (pointmin) (pointmax))) 65: (killbuffer "*Org ASCII Export*") 66: (deleteotherwindows) 67: (/ __x 200)))) ;; 200 words per minute reading 68: ;; ⟨2⟩ Article image 69: (my/orgstaticblogassembleimage it) 70: ;; ⟨3⟩ Preview 71: it 72: ;; ⟨4⟩ “Read more” link 73: (concat orgstaticblogpublishurl (fbase it)))) 74: (sjoin "\n\n")) 75: 76: ;; bottom matter 77: "\n#+begin_export html:\n" 78: "<hr><hr> <div id=\"archive\">" 79: "<a href=\"" 80: (orgstaticbloggetabsoluteurl orgstaticblogarchivefile) 81: "\">" (orgstaticbloggettext 'otherposts) "</a>" 82: "</div>" 83: "</div>" 84: "<div id=\"postamble\" class=\"status\">" 85: orgstaticblogpagepostamble 86: "</div>" 87: "\n#+end_export")) 88: (orgmode) 89: (orghtmlexporttohtml)))
The borderradius
property defines the radius of an
element's corners, we use it to make curvy looking source blocks.
Its behaviour changes depending on how many arguments it is given.
.src
and pre.src:before
are defined by Org.1: .src { 2: border: 0px !important; 3: /* 50px for topleft and bottomright corners; 4: 20px for topright and bottomleft cornerns. */ 5: borderradius: 50px 20px !important; 6: } 7: 8: pre.src:before { 9: /* border: 0px !important; */ 10: /* backgroundcolor: inherit !important; */ 11: padding: 3px !important; 12: borderradius: 20px 50px !important; 13: fontweight:700 14: } 15: 16: /* wrap lengthy lines for code blocks */ 17: pre{whitespace:prewrap} 18: 19: /* Also curvy inline code with ~ ⋯ ~ and = ⋯ = */ 20: code { 21: background: Cyan !important; 22: borderradius: 7px; 23: /* border: 1px solid lightgrey; background: #FFFFE9; padding: 2px */ 24: }
Code such as (= 2 (+ 1 1))
now sticks out with a cyan background ♥‿♥
table { background: pink; borderradius: 10px; /* width:90% */ borderbottom: hidden; bordertop: hidden; display: table !important; /* Put table in the center of the page, horizontally. */ marginleft:auto !important;marginright:auto !important; fontfamily:"Courier New"; fontsize:90%; } /* Styling for ‘t’able ‘d’ata and ‘h’eader elements */ th, td { border: 0px solid red; }
Prime  2^{Prime} 

1  2 
2  4 
3  8 
5  32 
7  128 
11  2048 
;; MA: Relocate this to my init. ;; Table captions should be below the tables (setq orghtmltablecaptionabove nil orgexportlatextablecaptionabove nil)
my/blog/newarticle
Helper function to make a new article.
1: (defvar my/blog/tags 2: '(emacs faith categorytheory ordertheory 3: lisp types packages haskell agda 4: c framac programproving) 5: "Tags for my blog articles.") 6: 7: ;; Use CSPC to select multiple items 8: 9: (defun my/blog/newarticle () 10: "Make a new article for my blog; prompting for the necessary ingredients. 11: 12: If the filename entered already exists, we simply write to it. 13: The user notices this and picks a new name." 14: (interactive) 15: (let (file desc) 16: 17: (threadlast orgstaticblogpostsdirectory 18: fentries 19: (mapcar #'ffilename) 20: (completingread "Filename (Above are existing): ") 21: (concat orgstaticblogpostsdirectory) 22: (setq file)) 23: 24: ;; For some reason, ‘findfile’ in the thread above 25: ;; wont let the completingread display the possible completions. 26: (findfile file) 27: 28: (insert "#+title: " (readstring "Title: ") 29: "\n#+author: " userfullname 30: "\n#+email: " usermailaddress 31: "\n#+date: " (formattimestring "<%Y%m%d %H:%M>") 32: "\n#+filetags: " (sjoin " " (helmcompread "Tags: " 33: my/blog/tags 34: :markedcandidates t)) 35: "\n#+fileimage: " (completingread 36: "Image: " 37: (mapcar #'ffilename (fentries "~/blog/images/"))) 38: "\n#+description: " 39: (setq desc (readstring "Article Purpose: ")) 40: "\n\n* Abstract :ignore: \n" desc 41: "\n\n* ???")))
The #+description
is exported as HTML metadata which is used to
‘unfurl’ a link to an article: When a link to an article is pasted
in a social media website, it unfurls into a little card showing
some information about the link, such as its image, description, and author.
#+description
lines;
I'd like to have a terse oneliner with a longer description in the
Abstract
heading.[Cu Cu] Cc Cb
1: ;; Override all minor modes that use this binding. 2: (bindkey* (kbd "Cc Cb") 3: (lambda (&optional prefix) 4: "Cc Cb ⇒ Publish current buffer 5: Cu Cc Cb ⇒ Publish entire blog 6: Cu Cu Cc Cb ⇒ Publish entire blog; rerendering all blog posts 7: (This will take time!) 8: " 9: (interactive "P") 10: (pcase (or (car prefix) 0) 11: (0 (orgstaticblogpublishfile (ffull (buffername)))) 12: ;; (browseurloffile (format "%s%s.html" orgstaticblogpostsdirectory 13: ;; (fbase (buffername)))) 14: ;; Apparently I have to publish the current buffer before trying 15: ;; to publish the blog; otherwise I got some errors. 16: (4 (orgstaticblogpublishfile (ffull (buffername))) 17: (orgstaticblogpublish)) 18: (16 ;; (orgstaticblogpublish t) ⇒ Crashes. 19: ;; Delete all .html files, except “about” 20: (threadlast (fentries "~/blog/") 21: (filter (and (equal (fext it) "html") 22: (not (member (fbase it) '("about"))))) 23: (map (fdelete it))) 24: ;; Publish as usual 25: (orgstaticblogpublishfile (ffull (buffername))) 26: (orgstaticblogpublish)))))
Line 23: To rerender an article, just remove its corresponding .html file ;)
The prefix al is the Arabic definite particle which may correspond to English's the; whereas basmala refers to a beginning.
That is, this is a variation on the traditional "hello world" ;)