Thirty degrees in Accra
Kofi lives in Accra, where the afternoon temperature is . His cousin Maya in Chicago uses the Fahrenheit scale, so Kofi converts with the rule his teacher gave him: multiply by , then add .
So it is . Maya wants to check, so she converts back. She subtracts to get , then divides by to get . Perfect. But her friend Sam tries “divide by , then subtract ” and gets
Sam used exactly the right two undoing steps — just in the wrong order — and got the wrong answer. This lesson explains why, and what “doing one thing after another” really means for functions.
Machines, tables and arrows
Think of a function as a machine: put an input in, and exactly one output comes out (see functions). Two machines can be placed in a row, so that whatever comes out of the first goes straight into the second. The whole row is a new machine.
The same chain can be shown with arrow diagrams. Here the first function sends each person to their home city, and the second sends each city to its continent. Following two arrows in a row sends each person to a continent.
Definitions
Definition— applying f, then g
Let and be functions. Applying , then sends each to
This defines a new function from to . Read from the inside out: first work out , then feed the result to .
The chain only makes sense if every output of is an allowed input of . If is only defined on some set , the chain “, then ” exists exactly when every value lies in .
Definition— identity function
For any set , the identity function is defined by . It returns every input unchanged — a machine that does nothing.
Definition— undoing and inverse functions
A function undoes if
If undoes and undoes , then and are inverse functions of each other, and we write .
Note
In the next lesson, on composition, “, then ” gets its own symbol, , read “ after ”. The order in the symbol matches the order in : the function written nearest to acts first.
Worked examples
Example— Chaining tables
With the functions from the arrow diagram:
| person | home city | continent |
|---|---|---|
| Amina | Kano | Africa |
| Bo | Chengdu | Asia |
| Carlos | Lima | South America |
| Dilnoza | Tashkent | Asia |
The chain “home city, then continent” sends Amina to Africa, Bo and Dilnoza to Asia, and Carlos to South America. Notice that Bo and Dilnoza have different home cities but end up in the same continent: a chain can combine outputs that were separate after the first step.
Example— Chaining formulas — and the order matters
Let and , both on the integers.
- “, then ”: .
- “, then ”: .
These never agree: and always differ by . With a third step, “add , then double, then subtract ” sends to : the long chain is just doubling.
Example— A chain that cannot be formed
Let on the rational numbers , and let , which is defined only for . The chain “, then ” would send to — but at the output of is , which does not accept. So the chain is a function on without , not on all of .
Example— Undoing
- “Subtract ” undoes “add ”, and “add ” undoes “subtract ”: they are inverses.
- On , “halve” undoes “double”, and “double” undoes “halve”: and .
- On , “square” cannot be undone: and , so a function undoing it would have to send back to both and .
Example— Undoing the temperature conversion
“Celsius to Fahrenheit” is the chain “multiply by , then add ” (and ). Its undoing steps are “divide by ” (that is, multiply by ) and “subtract ”, and they must be done in reverse order:
At this gives . Sam's order gives .
The rules of chaining and undoing
Theorem— Theorem 1 (doing nothing changes nothing)
For every function : “, then ” is , and “, then ” is .
Proof
Two functions are equal when they give the same output for every input. For each : “, then ” gives , and “, then ” gives .
∎
Theorem— Theorem 2 (when can adding and multiplying be swapped?)
Let and be integers. “Add , then multiply by ” and “multiply by , then add ” are the same function on exactly when or .
Proof
The first chain sends to ; the second sends to .
If , both are . If , both are . So in these cases the chains agree.
Conversely, suppose the chains agree for every . At this says , so . A product of two integers is only if one of them is , so or .
∎
So for most choices the order matters — Kofi's “multiply by , then add ” is not the same as “add , then multiply by ”.
Theorem— Theorem 3 (socks and shoes)
Suppose is undone by , and is undone by . Then “, then ” is undone by “, then ”.
Proof
Take any . Doing , then , then , then gives
The first step uses that undoes (applied to the input ), and the second that undoes .
∎
The proof shows why the order is forced: can only cancel if it comes directly after it. In Sam's order, “divide by ” arrives while the is still in the way.
Theorem— Theorem 4 (undoable functions keep inputs apart)
If some function undoes , then never sends two different inputs to the same output: whenever , we have .
Proof
Suppose . Apply to both sides: . Since undoes , the left side is and the right side is . So .
∎
In the language of the functions lesson, a function that can be undone is injective. Squaring on is not injective (), so by Theorem 4 nothing can undo it. Conversely, an injective function on a non-empty set can always be undone: send each output back to the input it came from, and send everything that is never an output anywhere you like.
Note
Undoing is not quite the same as being an inverse. On , “halve and round down” undoes “double”, because . But “double” does not undo “halve and round down”: is halved to , and doubling gives , not . In fact doubling has no inverse function at all, because odd numbers are never outputs, so nothing could be sent back to them. On , doubling and halving are true inverses. A function has an inverse function exactly when it is a bijection — injective and surjective.
Chains and undoing in Lean
In Lean we write the chain “, then ” as andThen f g. Lean only allows it when the output type of f is the input type of g, so the rule “every output must be an allowed input” is checked automatically.
/-- "Do `f`, then `g`": the function that sends `a` to `g (f a)`. It can only be formed when the
outputs of `f` (of type `β`) are inputs that `g` accepts. -/
def andThen {α β γ : Type} (f : α → β) (g : β → γ) : α → γ := fun a => g (f a)Lean's own library writes the same function as g ∘ f; the two agree by definition:
/-- Our `andThen f g` is the function Lean's library writes `g ∘ f`, read "`g` after `f`".
They agree by definition, so `rfl` proves it. -/
theorem and_then_eq_comp {α β γ : Type} (f : α → β) (g : β → γ) : andThen f g = g ∘ f := rflComputing chains, and seeing that the order matters:
/-- "Add 3, then double" sends `x` to `2(x + 3) = 2x + 6`. -/
theorem add_then_double (x : Int) : andThen (fun n => n + 3) (fun n => 2 * n) x = 2 * x + 6 := by
show 2 * (x + 3) = 2 * x + 6
omega/-- "Double, then add 3" sends `x` to `2x + 3`. -/
theorem double_then_add (x : Int) : andThen (fun n => 2 * n) (fun n => n + 3) x = 2 * x + 3 := by
show 2 * x + 3 = 2 * x + 3
rfl/-- **Order matters**: "add 3, then double" and "double, then add 3" are different functions.
One input is enough to tell them apart: at `0` they give `6` and `3`. -/
theorem order_matters :
andThen (fun n : Int => n + 3) (fun n => 2 * n) ≠ andThen (fun n : Int => 2 * n) (fun n => n + 3) := by
intro h
have h0 := congrFun h 0
rw [add_then_double, double_then_add] at h0
omega/-- **When can "add `a`" and "multiply by `m`" be swapped?** "Add `a`, then multiply by `m`" gives
`m(x + a) = mx + ma`, while "multiply by `m`, then add `a`" gives `mx + a`. They agree for every
`x` exactly when `ma = a`, that is, when `a = 0` or `m = 1`. -/
theorem add_mul_swap_iff (a m : Int) :
(∀ x, andThen (fun n => n + a) (fun n => m * n) x = andThen (fun n => m * n) (fun n => n + a) x)
↔ a = 0 ∨ m = 1 := by
constructor
· intro h
-- At `x = 0` the two chains give `m · a` and `a`.
have h0 : m * (0 + a) = m * 0 + a := h 0
have hma : (m - 1) * a = 0 := by
rw [Int.sub_mul, Int.one_mul]
rw [Int.zero_add, Int.mul_zero, Int.zero_add] at h0
omega
cases Int.mul_eq_zero.mp hma with
| inl hm => exact Or.inr (by omega)
| inr ha => exact Or.inl ha
· intro h x
show m * (x + a) = m * x + a
cases h with
| inl ha => rw [ha, Int.add_zero, Int.add_zero]
| inr hm => rw [hm, Int.one_mul, Int.one_mul]The identity laws are true “by definition”: Lean accepts rfl, which means both sides compute to literally the same function.
/-- Doing nothing first changes nothing: "identity, then `f`" is `f`. -/
theorem id_and_then {α β : Type} (f : α → β) : andThen id f = f := rfl/-- Doing nothing afterwards changes nothing: "`f`, then identity" is `f`. -/
theorem and_then_id {α β : Type} (f : α → β) : andThen f id = f := rflUndoing, and some examples on and (core Lean's Rat is the rational numbers):
/-- `g` **undoes** `f` if doing `f` and then `g` always gives back the input: `g (f a) = a`. -/
def Undoes {α β : Type} (g : β → α) (f : α → β) : Prop := ∀ a, g (f a) = a/-- "Subtract 3" undoes "add 3" … -/
theorem sub_undoes_add : Undoes (fun x : Int => x - 3) (fun x => x + 3) := by
intro a
show a + 3 - 3 = a
omega/-- … and "add 3" undoes "subtract 3". Each undoes the other: they are **inverse** functions. -/
theorem add_undoes_sub : Undoes (fun x : Int => x + 3) (fun x => x - 3) := by
intro a
show a - 3 + 3 = a
omega/-- On the rational numbers, "halve" undoes "double" and "double" undoes "halve": `x ↦ 2x` and
`x ↦ x / 2` are inverse functions of `ℚ`. -/
theorem halve_undoes_double :
Undoes (fun x : Rat => x / 2) (fun x => 2 * x) ∧ Undoes (fun x : Rat => 2 * x) (fun x => x / 2) := by
constructor
· intro a
show 2 * a / 2 = a
grind
· intro a
show 2 * (a / 2) = a
grind/-- On the integers, halving *with rounding down* (Lean's `y / 2`) still undoes doubling:
`(2x) / 2 = x`. This applies core Lean's `Int.mul_ediv_cancel_left`. -/
theorem int_halve_undoes_double : Undoes (fun y : Int => y / 2) (fun x => 2 * x) :=
fun x => Int.mul_ediv_cancel_left x (by decide)/-- … but on the integers doubling does **not** undo halving: `1` is halved (rounding down) to
`0`, and doubling `0` gives `0`, not `1`. So `x ↦ 2x` has no inverse function `ℤ → ℤ`: odd
numbers are never outputs, so nothing can be sent back to them. -/
theorem int_double_not_undoes_halve : ¬ Undoes (fun x : Int => 2 * x) (fun y => y / 2) := by
intro h
have h1 : 2 * (1 / 2 : Int) = 1 := h 1
exact absurd h1 (by decide)/-- **Socks and shoes.** If `f'` undoes `f` and `g'` undoes `g`, then "`f`, then `g`" is undone by
"`g'`, then `f'`": undo the **last** step **first**. The calculation:
`f' (g' (g (f a))) = f' (f a) = a`. -/
theorem undo_chain {α β γ : Type} {f : α → β} {g : β → γ} {f' : β → α} {g' : γ → β}
(hf : Undoes f' f) (hg : Undoes g' g) : Undoes (andThen g' f') (andThen f g) := by
intro a
calc andThen g' f' (andThen f g a) = f' (g' (g (f a))) := rfl
_ = f' (f a) := by rw [hg (f a)]
_ = a := hf a/-- Celsius to Fahrenheit: multiply by `9/5`, then add `32`. -/
def toFahrenheit : Rat → Rat := andThen (fun c => 9 / 5 * c) (fun x => x + 32)/-- Fahrenheit to Celsius: subtract `32`, then multiply by `5/9` — the undoing steps, in reverse
order. -/
def toCelsius : Rat → Rat := andThen (fun f => f - 32) (fun x => 5 / 9 * x)/-- A warm day: `30 °C` is `86 °F`, and converting back gives `30 °C` again. Lean checks the
fraction arithmetic exactly (`decide +kernel` evaluates both sides). -/
theorem thirty_degrees : toFahrenheit 30 = 86 ∧ toCelsius 86 = 30 := by
decide +kernel/-- `toCelsius` undoes `toFahrenheit` for **every** temperature. This is the socks-and-shoes rule
`undo_chain` applied to the two steps: `× 5/9` undoes `× 9/5`, and `− 32` undoes `+ 32`. -/
theorem to_celsius_undoes : Undoes toCelsius toFahrenheit := by
apply undo_chain
· intro c
show 5 / 9 * (9 / 5 * c) = c
grind
· intro x
show x + 32 - 32 = x
grind/-- Undoing the steps in the **wrong** order fails: "multiply by `5/9`, then subtract `32`" does
not undo `toFahrenheit`. At `0 °C`: `toFahrenheit 0 = 32`, and `5/9 · 32 − 32 = −128/9 ≠ 0`. -/
theorem wrong_order_fails :
¬ Undoes (andThen (fun x : Rat => 5 / 9 * x) (fun x => x - 32)) toFahrenheit := by
intro h
have h0 := h 0
revert h0
decide +kernelFinally, Theorem 4 and its consequence for squaring:
/-- If `f` can be undone, then different inputs always give different outputs: from
`f a = f b`, apply the undoing function `g` to both sides to get `a = g (f a) = g (f b) = b`. -/
theorem injective_of_undoes {α β : Type} {f : α → β} {g : β → α} (h : Undoes g f) (a b : α)
(hab : f a = f b) : a = b :=
calc a = g (f a) := (h a).symm
_ = g (f b) := by rw [hab]
_ = b := h bCore Lean's library already knows Theorem 4, under the names Function.LeftInverse (for “undoes”) and Function.Injective; this version just applies it:
/-- The same fact from core Lean's library, where "`g` undoes `f`" is called
`Function.LeftInverse g f` (it is defined exactly like our `Undoes g f`) and "never sends two
inputs to the same output" is `Function.Injective f`. This applies
`Function.LeftInverse.injective`. -/
theorem injective_of_undoes_library {α β : Type} {f : α → β} {g : β → α} (h : Undoes g f) :
Function.Injective f :=
Function.LeftInverse.injective h/-- **Squaring on ℤ cannot be undone**: `2² = (−2)² = 4`, so an undoing function would have to
send `4` back to both `2` and `−2`. -/
theorem square_cannot_be_undone : ¬ ∃ g : Int → Int, Undoes g (fun x => x * x) := by
intro ⟨g, hg⟩
have h : (2 : Int) = -2 := injective_of_undoes hg 2 (-2) (by decide)
exact absurd h (by decide)What is formalised: every theorem of the proof section and every example above, using core Lean only. The small arithmetic steps are finished by tactics: omega for whole-number arithmetic, grind for rearranging fractions, and decide +kernel, which makes Lean's trusted kernel evaluate exact fraction arithmetic such as . Mathlib, Lean's large mathematical library, builds on the same names: it proves, for instance, that a function has an inverse exactly when it is a bijection (Function.bijective_iff_has_inverse).
Chaining tables by computer
"""Chaining functions given by tables, and undoing chains in the right order.
Functions on finite sets are stored as Python dictionaries, so "f, then g"
is computed by looking up g[f[a]]. The experiment chains two tables, then
runs through all 27 functions from {0, 1, 2} to itself: it checks the
identity laws, finds which functions can be undone (exactly the 6 that never
send two inputs to the same output), and tests the "socks and shoes" rule
for undoing a chain on every pair of them - and how often undoing in the
WRONG order happens to work. Last, it converts temperatures exactly with
fractions. Exact, exhaustive-finite evidence.
"""
from fractions import Fraction
from itertools import product
def then(f, g):
"""The table of 'f, then g': a -> g[f[a]]. Every output of f must be an
input of g, otherwise the chain is not defined."""
missing = [b for b in f.values() if b not in g]
if missing:
raise ValueError(f"outputs {missing} are not inputs of the second table")
return {a: g[f[a]] for a in f}
def identity(elements):
"""The identity function on a finite set, as a table."""
return {a: a for a in elements}
def undoes(g, f):
"""True when g undoes f: g[f[a]] == a for every input a of f."""
return all(f[a] in g and g[f[a]] == a for a in f)
def all_functions(elements):
"""Every function from the finite set to itself (n**n of them)."""
elements = list(elements)
for outputs in product(elements, repeat=len(elements)):
yield dict(zip(elements, outputs))
def undoing_function(f, elements):
"""A function g on `elements` that undoes f, or None if there is none."""
for g in all_functions(elements):
if undoes(g, f):
return g
return None
def to_fahrenheit(c):
"""Multiply by 9/5, then add 32."""
return Fraction(9, 5) * c + 32
def to_celsius(f):
"""Subtract 32, then multiply by 5/9: the undo steps in reverse order."""
return Fraction(5, 9) * (f - 32)
def wrong_undo(f):
"""Multiply by 5/9, then subtract 32: the undo steps in the wrong order."""
return Fraction(5, 9) * f - 32
HOME_CITY = {"Amina": "Kano", "Bo": "Chengdu", "Carlos": "Lima",
"Dilnoza": "Tashkent", "Emma": "Oslo"}
CONTINENT = {"Kano": "Africa", "Chengdu": "Asia", "Lima": "South America",
"Tashkent": "Asia", "Oslo": "Europe", "Perth": "Oceania"}
def main():
print("1. Two tables in sequence: home city, then continent")
for person, place in then(HOME_CITY, CONTINENT).items():
print(f" {person:<8} -> {HOME_CITY[person]:<9} -> {place}")
print()
elems = [0, 1, 2]
funcs = list(all_functions(elems))
ident = identity(elems)
ok = all(then(ident, f) == f and then(f, ident) == f for f in funcs)
print(f"2. All {len(funcs)} functions from {{0, 1, 2}} to itself")
print(" identity, then f == f == f, then identity, for all f:", ok)
pairs = sum(then(f, g) == then(g, f) for f in funcs for g in funcs)
print(f" pairs (f, g) where the order does not matter: {pairs} of 729")
undo = [(f, undoing_function(f, elems)) for f in funcs]
undo = [(f, u) for f, u in undo if u is not None]
print(f" functions that can be undone: {len(undo)}")
right, wrong, swap = 0, set(), set()
for (i, (f, uf)), (j, (g, ug)) in product(enumerate(undo), repeat=2):
right += undoes(then(ug, uf), then(f, g))
if undoes(then(uf, ug), then(f, g)):
wrong.add((i, j))
if then(f, g) == then(g, f):
swap.add((i, j))
total = len(undo) ** 2
print(f" 'f, then g' undone by 'undo g, then undo f': {right} of {total}")
print(f" undone by 'undo f, then undo g' (wrong order): {len(wrong)} of {total}")
print(f" ... and those {len(wrong)} are exactly the pairs where f and g")
print(f" can be swapped: {wrong == swap}")
print()
print("3. Temperatures, with exact fractions")
f = to_fahrenheit(30)
print(f" 30 C -> {f} F -> back: {to_celsius(f)} C (right order)")
print(f" wrong order gives {wrong_undo(f)} C, about {float(wrong_undo(f)):.1f}")
print()
print("Epistemic status: exact and exhaustive for the 27 functions on a")
print("3-element set. The lesson proves the socks-and-shoes rule for all")
print("functions. That the wrong order works exactly when f and g can be")
print("swapped is checked here for 36 pairs only; exercise: prove it.")
if __name__ == "__main__":
main()Three things to notice. First, the order of a chain matters for most pairs: only of the pairs give the same function in both orders. Second, exactly functions can be undone — the ones that never send two inputs to the same output, as Theorem 4 predicts. Third, undoing in the right order works for all pairs (Theorem 3), while undoing in the wrong order works for exactly the pairs whose order can be swapped. The computation shows that pattern for this small set; one of the exercises asks you to prove it.
Explore: composition machines
Choose two machines and feed in a number. The widget runs them in both orders, so you can see at once whether the order matters. The “Arrow chains” tab shows chains of tables between small sets, as in our arrow diagram.
Composition machines
Feed a number through two machines. Does the order matter?
g ∘ ff first, then g
Add 1
x ↦ x + 1
→ 4
Double
x ↦ 2x
→ 8
f ∘ gg first, then f
Double
x ↦ 2x
→ 6
Add 1
x ↦ x + 1
→ 7
Different results: 8 versus 7. The order of composition matters.
Things to try:
- Find a pair of machines whose order does not matter. (Does every pair involving “Do nothing” work? Why — which theorem is that?)
- “Square” and “Opposite”: does the order matter? Try a negative input.
- Find a machine that undoes itself: doing it twice gives back your input.
- “Add 1” then “Subtract 3”: which single machine would do the same as this chain?
Exercises
1. Function machines in sequence
Warm-upHumanLet and . Apply , then , to the input . What is the output?
2. Function machines in sequence
CoreHumanLet be given by , , , , and by , , . Which inputs does “, then ” send to ? List them.
3. Function machines in sequence
CoreHumanWhich of these chains is defined for every integer input ? (Here accepts only inputs , and only inputs .)
4. Function machines in sequence
CoreHumanA function is the chain “multiply by , then subtract ”. Which chain undoes it?
5. Function machines in sequence
StretchHumanFrom Bhāskara II's Līlāvatī (1150): a number is multiplied by ; then three quarters of the result is added to it; the sum is divided by ; a third of the quotient is subtracted from it; the result is multiplied by itself; is subtracted; the square root is taken; is added; and the result is divided by . The answer is . What was the number?
6. Function machines in sequence
CoreHumanWhich of these functions from to can be undone by some function from to ? Choose all that apply.
7. Function machines in sequence
CoreHumanFor which integer do “add , then multiply by ” and “multiply by , then add ” give the same output for every integer input?
8. Function machines in sequence
StretchHumanLet and be functions from a set to itself with inverse functions and . Undoing “, then ” in the wrong order means applying , then , so each input goes to . When does this give back for every input?
9. Function machines in sequence
CoreFormalIn the Lean section, Undoes g f means for every . Which Lean statement says “squaring on cannot be undone”?
10. Function machines in sequence
StretchComputationalA function on a finite set can be stored as a dictionary, such as {1: 'a', 2: 'b'}. Write invert(table) that returns a dictionary undoing table — sending each output back to its input — or None if two inputs share an output (so the function cannot be undone).
Common misconceptions
Misconception
Writing “first , then ” as . In you must work out first, so acts first. “First , then ” is : the function nearest to is the one that acts first.
Misconception
“Every function can be undone.” Squaring on sends and to the same output, , so no function can send back to the right place (Theorem 4). The same goes for any function that sends two inputs to one output, such as “round to the nearest ten”.
Misconception
“To undo a chain, undo each step in the same order.” Undo the last step first: socks, then shoes, is undone by shoes off, then socks off. “Multiply by , then add ” is undone by “subtract , then divide by ”.
Misconception
Ignoring whether outputs are allowed inputs. “Subtract , then take the reciprocal” breaks at , because is not defined. Before chaining, check that every output of the first function is an input the second one accepts.
Misconception
“If two chains agree for one input, they are the same function.” “Add , then double” and “double, then add ” agree everywhere, but “add , then square” and “square, then add ” agree only at . Functions are equal only when they agree for every input.
Other ways of seeing
Algebra— Undoing in reverse, everywhere
The functions from a set to itself that can be undone in both directions — the bijections — can be chained, every one has an inverse, and the identity does nothing. A collection of reversible transformations like this is a group, and Theorem 3 becomes the rule: the inverse of “, then ” is “, then ”. Anyone who solves a Rubik's cube uses it: to undo the moves , then , you do backwards and then backwards. The same reversal appears with matrices, where , and in solving equations: to solve , undo “add , then double” by halving first and subtracting second.
Geometry— Moves of a square
Rotations and reflections of a square are functions from the plane to itself, and they can be chained. Turning a square a quarter turn anticlockwise and then reflecting it in its vertical axis gives a different result from reflecting first and then turning — the corner that ends up at the top right is different. But two rotations about the centre can always be swapped. Which moves can be swapped with which is the beginning of the study of symmetry (see symmetry and transformations).
Computation— Pipelines and the undo button
Programs are built from chained functions: read the data, then clean it, then sort it, then display it. A text editor's undo button keeps a stack of the actions you have performed and undoes the most recent one first — socks and shoes again. Computer scientists call this “last in, first out”.
A glimpse of structure
Picture each set as a dot and each function as an arrow between dots. Chaining is following arrows head to tail, and it is only allowed when one arrow ends where the next begins. Every dot has a “do nothing” arrow, and Theorem 1 says that following it changes nothing. In the next lesson you will also see that it never matters how you group a long chain. Dots and arrows with exactly these features form a category, and sets with functions are the first example. The Arrows that compose trail lets you build chains in a small category of your own.
Wonder Trail
History and global context
- Working backwards through a chain of operations is an old technique. Āryabhaṭa's Āryabhaṭīya (499 CE) states the method of inversion: multipliers become divisors and divisors multipliers, additions become subtractions and subtractions additions, working from the end.
- Bhāskara II's Līlāvatī (1150) contains a famous problem: a number is multiplied by , increased by three quarters of the product, divided by , decreased by a third of the result, multiplied by itself, decreased by ; then its square root is taken, is added and the result divided by , giving . What was the number? Undoing the steps from the last to the first gives the answer, (try it in the exercises).
- The word function was used by Gottfried Wilhelm Leibniz in the 1690s, and Leonhard Euler introduced the notation in 1734. The modern idea of a function as any rule that gives exactly one output for each input is usually credited to Peter Gustav Lejeune Dirichlet (1837).
Further reading
- F. William Lawvere and Stephen H. Schanuel, Conceptual Mathematics: A First Introduction to Categories (2nd edition, 2009), which begins with sets, maps and composition, including chains of maps drawn as arrows.
- Israel M. Gelfand, Elena G. Glagoleva and Emmanuel E. Shnol, Functions and Graphs (1969) — a classic problem book for school students.
- Richard Rusczyk, Introduction to Algebra (2007), the chapter on functions, including composition and inverses.