
The category design pattern - joubert
http://www.haskellforall.com/2012/08/the-category-design-pattern.html
======
lmm
An abstraction is only useful if you can use it.

What sold me on applicatives was being able to write "traverse", and similar
operations, that would be m * n if you had to write them for each
(traversable, applicative) pair but can be m + n instead (
[http://m50d.github.io/2013/01/16/generic-
contexts.html](http://m50d.github.io/2013/01/16/generic-contexts.html) ).
That's a very concrete code saving. Having three examples of things that form
categories is great - but can we show a real-world function that we can write
that takes a generic category, that does something useful with each of these
three examples? That would demonstrate that we're getting real value (i.e. a
code saving) out of the abstraction.

~~~
Tekmo
The generic benefit of a category is the proof that it satisfies the identity
and associativity laws. This lets you write "generic proofs" that work for any
category.

For example, take the following example type:

    
    
        newtype Example f c a b = Example (f (c a b))
    
        instance (Applicative f, Category c) => Category (Example f c) where
            id = Example (pure id)
    
            Example f . Example g = Example (liftA2 (.) f g)
    

You can prove that if `c` satisfies the `Category` laws and `f` satisfies the
`Applicative` laws, then `Example f c` also satisfies the `Category` laws.

This in turn lets you take any `Category` and keep extending it with as many
`Applicative`s as you want in any order, confident in the knowledge that your
extended `Category` still satisfies the `Category` laws.

This is a common pattern in code that is organized around category theory
and/or abstract algebra and I wrote a post showing a simpler example of this
phenomenon (using `Monoid` instead of `Category`):
[http://www.haskellforall.com/2014/07/equational-reasoning-
at...](http://www.haskellforall.com/2014/07/equational-reasoning-at-
scale.html)

~~~
lmm
You're just telling me how the abstraction gives rise to more abstractions.
That seems to be getting even further away from where the rubber hits the
road. I want to see a program that _does something directly useful_ ,
something there's an actual _business case_ for, that would take more code to
implement without the Category abstraction.

~~~
Kutta
Categories are just an utterly pared-down notion of composability, so most of
the time you don't even recognize them in the wild (like the associativity of
monads, which on an abstract level says that only the sequence of instructions
matters, but not their grouping - it's something that you expect to be true in
any imperative language!), or only recognize the absence of categories as
horrible design.

If you haven't already, you should play around with the pipes library, because
it makes very heavy use of categories and it's an extreme positive example of
composability.

As another practical example, Haskell's class constraints form a thin
category, with constraint entailment as arrows. This means that instance
resolution never depends on ordering of imports, local bindings, or basically
anything other than the raw set of available instances, and also any two
instances at the same type must be equal. This buys us great scalability and
refactorability, most notably in comparison to Scala, where alphabetizing
module imports sometimes breaks programs.

That said, in category theory one rarely works with vanilla categories but
rather other abstractions formulated in categorical language. Similarly, in
Haskell vanilla categories aren't as useful as the many well-known categorical
abstractions (basically, the core Functor - Applicative - Monad hierarchy is
directly category-theory inspired, and they've proven to be insanely useful).

~~~
lmm
I don't think the class example works, because you wouldn't operate on a class
hierarchy in code. Pipes is all well and good but unless I'm implementing it
myself I don't need to care about the examples.

If you want to sell this to ordinary programmers you need to take another step
down to the concrete level. Show two generic functions that take a category
and show how they are useful for two different categories (heck, I think I can
think of one myself: it should be easy to write a function that takes a type-
aligned list [c a b, c b d, ... c y z] and combines them as a single c a z).
If it's a useful abstraction then that really shouldn't be hard.

------
dkural
The ironic thing about this post is, no actual category theory is used.
Defining functions and functional composition laws is done (and is a perfectly
natural thing to do), at the beginning of any undergrad math class. The
definition of functions and composition laws was done more than a full century
before category theory.

Category theory was invented for a deeper reason - to have a rigorous
definition of a natural transformation, to enable a precise language to
translate back and forth between algebra and topology.

Note that this is not a comment about the usefulness of category theory in
theory of programming languages, it is about the uselessness of the linked
article in talking about it.

As another commenter pointed out, in practice Haskell functions as values
don't even form a category. This actually matters as illustrating the
practical difficulties involved.

As a mathematician, I'd say most practicing computer scientists have a very
skewed/warped understanding of category theory, and in their ignorance of
other theoretical concepts, tend to misunderstand what they do think they know
about. In general defining what you're trying to solve, tethered to a real,
meaty problem, and then judging theoretical formulations for succeeding at
these use cases is a good way to not get stuck in 'empty abstractions'. New
mathematics gets invented (and judged) as a way of solving problems. The chain
of reasoning might be long but it's there. People don't invent new terminology
for no reason (and even if they did, and it didn't help for understanding a
problem, it'd get no traction)

~~~
Tekmo
Did you read the entire post? Functions were only the first example to
introduce the reader to the basic idea of associativity and identity. The post
then discusses the Kleisli category and the category of coroutine pipelines.

~~~
dkural
I did read the entire post. Isomorphisms and homomorphisms between groups also
obey associativity, identity, can be composed etc - but there is no point to
talking about the 'category of groups', as opposed to, simply, Group theory
unless I somewhere use the fact that it's a category, in say, talking about
the topological spaces, and the fundamental group etc.

Did you read my comment? I claim that because something is associative and has
identity, concepts clarified and used in the 19th century, is not sufficient
motivation to bring about category theory. It's not used for anything in this
article.

~~~
tome
You are correct, but it seems a bit much to take the author to task about
this. Isn't all that you ask for to replace the words "category theory" with
"the notion of category" throughout the document?

------
tyre
This is wonderful, and correct, theory. As the Futurama joke goes, "you are
technically correct; the best kind of correct."

What the Haskell community keeps running into is that most people don't think
in theories. Trying to prove the theory to be practical has a poor success
rate. They need to implement useful things that people love, then show how the
theory got them there naturally.

Sentences like this:

> Category theory codifies this compositional style into a design pattern, the
> category.

To the average person, a theory codifying a style into a pattern just isn't
useful. What does that do? Monads are a common joke to this effect. They are
wonderfully useful but poorly understood by most developers. It isn't being
right that matters if few understand you.

Why was Rails successful?

Sure Ruby is nice, but Python is very similar (and it wasn't the `do...end`
blocks.)

It's because Rails could be used to easily build functional CRUD apps —
Whoops! it works — that made difficult easy. Sandy Metz doesn't get heard if
DHH isn't building Basecamp.

As a philosopher, it pains me to know people don't think that way, but if you
aren't changing people's lives then all the theory is just mental
masturbation.

~~~
mbrock
Haskell isn't designed for "average people" or "most people" and why should it
be?

Real human beings use Haskell every day and love it.

Real human beings are capable of learning about teh scary maths and abstract
theories.

You studied philosophy? Are you aware that most people don't understand
anything about philosophical theories other than The Secret? Have you thought
about how maybe those theories can change people's lives even though they're
not aimed at a lowest common denominator? E.g. by being influential in the
long run, and also by being intellectually stimulating to people who do bother
to learn about it?

Rails-style success is not the goal for Haskell. It is an explicit anti-goal:
the motto is "avoid success at all costs."

Terse and rude counterpoint to your thesis: Average people are lazy idiots and
designing your language to appeal to them will result in a very popular
incoherent mess, like Java and Ruby, and Dijkstra will make pointed remarks at
you from his grave.

[http://www.paulgraham.com/avg.html](http://www.paulgraham.com/avg.html)

~~~
omginternets
>Haskell isn't designed for "average people" or "most people" and why should
it be?

Although it isn't stated as such, I think the point is that the haskell
community is, on the whole, truly awful at pedagogy.

I'm perfectly fine with the notion that Haskell isn't for everyone, but being
a poor pedagogue is always a bad thing.

~~~
mbrock
If you're a good pedagogue, please help out.

Teaching isn't easy, and the abundance of poor pedagogy is surely a big reason
why so many people develop mathematical anxiety.

[https://en.wikipedia.org/wiki/Mathematical_anxiety](https://en.wikipedia.org/wiki/Mathematical_anxiety)

> Students often develop mathematical anxiety in schools, often as a result of
> learning from teachers who are themselves anxious about their mathematical
> abilities in certain areas. Typical examples of areas where mathematics
> teachers are often incompetent or semi-competent include fractions, (long)
> division, algebra, geometry "with proofs", calculus, and topology.

The programming world lately is inundated with tutorials that take their
pedagogical style from the Teletubbies or at best Bob Ross. Maybe that is not
a viable way of teaching Haskell—maybe there will need to be actual study
involved, with textbooks and exercises and trained teachers. I don't really
know.

[https://www.cs.utexas.edu/~EWD/transcriptions/EWD10xx/EWD103...](https://www.cs.utexas.edu/~EWD/transcriptions/EWD10xx/EWD1036.html)
("On the Cruelty of Really Teaching Computer Science")

> So, if I look into my foggy crystal ball at the future of computing science
> education, I overwhelmingly see the depressing picture of "Business as
> usual". The universities will continue to lack the courage to teach hard
> science, they will continue to misguide the students, and each next stage of
> infantilization of the curriculum will be hailed as educational progress.

~~~
omginternets
>If you're a good pedagogue, please help out.

I truly think I am, but I'm still struggling to learn Haskell...

>the abundance of poor pedagogy is surely a big reason why so many people
develop mathematical anxiety.

I'm one of them, but 28 years later, I'm finally realizing that mathematics
actually aren't _that_ hard.

>The programming world lately is inundated with tutorials that take their
pedagogical style from the Teletubbies or at best Bob Ross. Maybe that is not
a viable way of teaching Haskell.

I think you hit the nail on the head, here, and I would like to hazard the
following opinion: this is a deeply cultural problem in the United States,
where we operate under the implicit belief that learning should be _fun_.

I don't mean to suggest that learning isn't rewarding, deeply satisfying, and
occasionally exhilarating, but beyond learning your ABCs, it's rarely _fun_ in
the traditional sense. The problem, as I see it, is that we're failing tell
the truth: learning non-trivial things is tedious, grueling, difficult, and
tooth-grindingly frustrating while still being one of the most worthwhile
things in life. Instead, we try to teach difficult or non-obvious concepts
with the pedagogical equivalent of coloring books.

Stated differently, we simultaneously tell our youth that one has to "work
hard" and "make sacrifices" for things like sports, while telling them that
intellectual pursuits should be nothing less than blissfully entertaining.

As a general rule, I avoid anything labeled as being "for beginners", unless
it's from a foreign editor. I'm lucky to be a fluent speaker of French, and
I've noticed that French textbooks systematically approach subject matters in
a rigorous, Cartesian, matter-of-fact way ... and this from grade 1!

You want to know how I finally grokked monads? I read this paper:
[http://repository.cmu.edu/cgi/viewcontent.cgi?article=2846&c...](http://repository.cmu.edu/cgi/viewcontent.cgi?article=2846&context=compsci)

It sucked. I mean it was awful. I spent weeks pouring over this thing,
fighting the kind of frustration I described above.

And I got a D+ in undegraduate pre-calculus. I'm not a "math guy", but like
everybody else, coloring books don't cut it after you start growing armpit
hair. Big-boy concepts require big-boy methods.

~~~
mbrock
Excellent quotation in the intro to that paper:

> ... Our intention is not to use any deep theorems of category theory, but
> merely to employ the basic concepts of this field as organizing principles.
> This might appear as a desire to be concise at the expense of being
> esoteric. But in designing a programming language, the central problem is to
> organize a variety of concepts in a way which exhibits uniformity and
> generality. Substantial leverage can be gained in attacking this problem if
> these concepts are defined concisely within a framework which has already
> proven its ability to impose uniformity and generality upon a wide variety
> of mathematics.

(John Reynolds, "Using category theory to design implicit conversions and
generic operators", 1980.)

~~~
omginternets
I'm truly glad that you're enjoying that paper :)

This is completely unrelated, but I found this paper to be similarly brilliant
in its simplicity, clarity, and rigorous explanation:
[https://ramcloud.stanford.edu/raft.pdf](https://ramcloud.stanford.edu/raft.pdf)

~~~
zeckalpha
That paper took 25 years to refine. Additionally, for the right audience, more
math could make it more succinct.

~~~
omginternets
I don't doubt it, but I'm not sure what you're trying to say. Would you
elaborate?

~~~
catnaroek
I have a hard time already convincing myself that correct sequential
imperative algorithms are indeed correct - which probably means that I'm a bad
programmer, but I can't change who I am. So convincing myself that that Raft,
a concurrent algorithm, is correct (meets its specification) from an informal
description is completely out of question - where is the formal proof of
correctness?

~~~
mbrock
Take a look here for pointers to formal proofs:

[https://news.ycombinator.com/item?id=10017549](https://news.ycombinator.com/item?id=10017549)

Specifically Verdi, a Coq framework for distributed system verification, which
now includes a formally proven Raft implementation.

[https://github.com/uwplse/verdi](https://github.com/uwplse/verdi)

This appears to be the main theorem, "raft_linearizable":

[https://github.com/uwplse/verdi/blob/master/raft-
proofs/EndT...](https://github.com/uwplse/verdi/blob/master/raft-
proofs/EndToEndLinearizability.v)

~~~
catnaroek
Thank you very much. :-)

------
spooningtamarin
Or in other words. Mathematicians have been thinking of ways to solve problems
much longer than anyone else, they have formalized it, they developed
amazingly general methods.

There's a lot to learn from them.

~~~
qcoh
But just using the definitions is cargo cult and using abstractions for the
sake of using abstractions is not necessarily good mathematics. Currently it
looks like "use this design pattern because mathematics".

~~~
spooningtamarin
category theory is excellent mathematics useful in various areas of physics
(if you're looking for proof of application)

why wouldn't it be useful in programming?

~~~
qcoh
It's useful because it's a generic design pattern not because of the
mathematics behind it (of which virtually nothing is actually used, at least
in everyday programming).

~~~
spooningtamarin
Yeah, generic design pattern which is minimal, compact and simple.

Yeah, why would I use mathematics in programming.

~~~
qcoh
> Yeah, generic design pattern which is minimal, compact and simple.

A good argument for monads. Much better than the dishonest appeal to
mathematics.

~~~
tome
I think you're reading more into spooningtamarin's posts than was actually
there.

~~~
spooningtamarin
Really?

Yes, I definitely mentioned mathematicians, because they were the ones who
showed us the problem solving methods, the modeling/design method.

I'm definitely agreeing that one uses practically none of the category theory
in programming. But let's say that we can be influenced by the design of
category theory, and get some theorems for free because of it.

------
calhoun137
I have been studying Category theory recently, on the surface, its merely a
theory about dots that are connected by arrows.

For some reason, category theorists often claim that category theory is "more
fundamental" than set theory, whatever that means. It's especially bizarre
since many category theory books begin by saying they will define categories
without using sets, and then proceed to say "let A be a collection of dots,
and B a collection of arrows" but only God knows what the difference between a
collection and a set is, from what I can tell...

~~~
zeckalpha
Sets are more than a collection. They have axioms that are not needed to
define categories.

~~~
calhoun137
Mathematicians used the "naive" concept of a set as "a collection of objects"
for thousands of years, and it led to a major crisis in the foundations of
mathematics that was only resolved by establishing a rigorous system of
axiom's that define what a set is.

So I disagree that a "set is more than a collection", with the qualification
that the concept of a "collection" that is not associated with any axioms
whatsoever, is meaningless. Why do I say "meaningless"? Because this "naive"
concept of a collection leads to a number of logical paradox's that can only
be resolved by using a system of axioms to define these collections.

So it's true that you can define sets by various axiom systems that are not
the same, but these systems all exist under the umbrella of "set theory".

Using the "naive" concept of a collection is fair enough, since we all "know
what you mean", but only as long as your point isn't that category theory is
more fundamental than set theory.

~~~
zeckalpha
Actually, it didn't resolve the crisis. In fact, categories, computability
theory, and most mathematical findings of the last 150 years are a direct
result of stepping around the crisis, namely Russell's paradox.

My point is not that category theory is more fundamental than set theory
(though set theory can be described in terms of categories), but rather that
it isn't derivative. You don't need the formalisms of set theory to have
category theory.

------
auvrw
i once had a conversation that ended with "i don't know enough category theory
to be good at haskell."

after learning a (little) more category theory and a (very little) more
haskell, i think that assessment was inaccurate, and i wouldn't want anyone
else to think the same thing.

haskell is strictly-typed, sugared lambda calculus. if you want to
learnyouahaskell, think about types and type classes. learn how to desugar
wheres, lets, guards, etc. don't worry about functors, much less monads.

~~~
tome
> i once had a conversation that ended with "i don't know enough category
> theory to be good at haskell."

> after learning a (little) more category theory and a (very little) more
> haskell, i think that assessment was inaccurate, and i wouldn't want anyone
> else to think the same thing.

Indeed. I work with some of the most highly-regarded Haskellers that exist.
Few of them would claim they know much (or any?) category theory, nor do they
care about category theory.

~~~
nbouscal
I don't know who the Haskellers you refer to are, or who they're regarded
highly by, but all of the core members of the Haskell community that I know do
care about category theory, and do know some amount of it. It's too useful to
ignore.

You absolutely do not need to know category theory to use Haskell effectively
and productively. Once you've been using it for a while, though, you pretty
much inevitably start learning (and caring about) the mathematics that
underlies it.

~~~
tome
I'm pretty sure most of the following would say they don't know much (or any)
category theory:

Simon PJ, Simon Marlow, BoS, Lennart Augustsson, Don Stewart, Duncan Coutts,
Neil Mitchell, Roman Leshchinsky

That hasn't stopped them producing masses of great Haskell.

I would welcome correction or clarification by any of the above luminaries!

~~~
auvrw
> Don Stewart

he wrote my window manager!

going out on a limb, i guess these people would say that they don't know much
category theory because they understand that category theory contains
_results_ , not just definitions. moreover, it bleeds into algebra, geometry,
k-theory, etc. pretty fast. even some /very/ respectable mathematicians (e.g.
matsumura) are hesitant to say they know much about all of these things.

i think it's really cool that haskell makes programmers interested in math.
i'm still pretty sure that everything about it that has anything to do with
category theory is contained in the Prelude or on hackage, tho. in other
words, all the category theory stuff that's "built-in" to haskell is itself
expressed in haskell. at the risk of raising some hairs: at one point
Crockford mentioned that making monads is possible in javascript, too.

~~~
tome
> > Don Stewart

> he wrote my window manager!

Mine too!

To be clear, I would _guess_ (and I'm by no means sure) that these very highly
regarded Haskellers barely know how to define "category" and "functor" (the
mathematical versions, not the Haskell versions) and certainly don't know how
to define "natural transformation". I admit the following as evidence:
[https://twitter.com/bos31337/status/656319244263518208](https://twitter.com/bos31337/status/656319244263518208)

I think it's _very_ important that we, as the Haskell community, reaffirm at
every possible opportunity that you do _not_ need to know category theory, or
even much mathematics, to succeed at Haskell.

------
arnia
If people are interested in reading more about how category theory can be used
to help define the structure of software, there are two excellent books I can
recommend.

The first, 'Computational Category Theory' by Rydeheard and Burstall, builds a
library (in Standard ML) that allows one to construct objects that satisfy
properties you care about (defined in category theory terms). For example, you
could define a rules system in a fuzzy logic and have the right code be
automatically generated using the library.

The second is less concrete, but probably more eyeopening. 'Category Theory
for Computing Science' by Barr and Wells starts from the basic axioms of a
category and ends with defining all sorts of interesting patterns for software
modularity based on diagrammatic reasoning (a common tool in category theory
for proving properties through simple pictures). The ideas here have helped me
simplify and think more clearly about code I've been writing (in languages
like Java, Python, and Ruby), even if the code isn't (ostensibly) categorical.

------
panic
Note that Haskell functions, when considered as values, do not actually form a
category: [https://wiki.haskell.org/Hask](https://wiki.haskell.org/Hask)

~~~
dkbrk
I believe that any such problems are due to non-totality and/or non-strict
semantics, and generally aren't important in practice. For example, from the
abstract for "Fast and Loose Reasoning is Morally Correct"
([http://www.cs.ox.ac.uk/jeremy.gibbons/publications/fast+loos...](http://www.cs.ox.ac.uk/jeremy.gibbons/publications/fast+loose.pdf)):

    
    
      Two languages are defined, one total and one partial, with identical
      syntax. The semantics of the partial language includes partial and infinite
      values, and all types are lifted, including the function spaces. A partial
      equivalence relation (PER) is then defined, the domain of which is the total
      subset of the partial language. For types not containing function spaces the
      PER relates equal values, and functions are related if they map related values
      to related values.  It is proved that if two closed terms have the same
      semantics in the total language, then they have related semantics in the
      partial language. It is also shown that the PER gives rise to a bicartesian
      closed category which can be used to reason about values in the domain of the
      relation.

------
draw_down
As I expected, I can follow it until it gets to monads. The thing about
explaining monads isn't even a joke anymore, it's just become mundane reality

It says that (.) is the same thing as (>=>). Fine, but then why are we talking
about (<=<) and (=<<) instead of (id) and (.) now?

~~~
tome
The point of the article is that even though function composition and monadic
composition are made out of different things ((.) and id versus (<=<) and
return respectively) they have the same underlying structure: associative
composition with identity.

------
ToJans
For me this has been really useful.

While I somewhat have a noob understanding of Haskell, and some experience
with monads, the parallels between composing functions (i.e. "id" & (.) ) and
composing monads i.e. "return" & (<=<) never really occurred to me. Now that
I've seen this, I'll probably start looking for similar patterns in other
places.

This has been what made Haskell click for me: the minute you start doing
something that is slightly repetitive, like a for loop including a
continue/break, parallelizing code or using if error/null all the time,
there's a pattern there. Haskellers recognize these patterns, and implement
them once, so you can reuse them and avoid making mistakes by implementing
something similar yourself every single time. Having this opportunity makes
your code more shorter and concise, and instead of implementing something
yourself, you can use these battle-tested implementations to model your code.
(And thus avoid those annoying not-null/if error bugs for example.)

Even though Haskell might not be suitable for every problem domain, I found
out it really shines by it's explicitness. You model your domain using types
for intermediate states, and the exercise of converting one type to another is
rather trivial. (f.e. a sudoku solver [3]).

The biggest problem for me personally has been my inability to speak the
category theory language, but I'm learning... I also believe that the basic
Haskell libs need examples next to their descriptions, because most of us
learn by example. [1] If I can see what a maybe monad is for, I can start
using it without properly understanding monads. After a while you'll have seen
enough different types of monads to understand their use case. IMO one
language implementer that understood this extremely well is José Valim, just
take a look at the Elixir docs for data.list. [2]

Because Haskell has been mostly implemented by mathematicians and computer
scientists, they assume basic upfront knowledge. This is what makes it hard
for beginners. What we really need in the Haskell world is some cognitive bias
mitigation. YMMV.

* [1]: [https://www.reddit.com/r/haskell/comments/3rhpyq/as_a_haskel...](https://www.reddit.com/r/haskell/comments/3rhpyq/as_a_haskell_noob_i_think_docs_need_more_examples/) * [2]: [http://elixir-lang.org/docs/stable/elixir/List.html#function...](http://elixir-lang.org/docs/stable/elixir/List.html#functions) * [3]: [https://github.com/ToJans/learninghaskell/blob/master/0003%2...](https://github.com/ToJans/learninghaskell/blob/master/0003%20Sudoku.hs)

------
cousin_it
Function composition and its generalizations are cool, but let's not blind
ourselves and claim that they're the key ingredient for good programming.

We should be looking for more ideas that make programming better with minimal
learning cost. Examples of such ideas that were wildly successful in the past:
garbage collection, modules and imports, Unicode strings. Each of those was
justified by practice rather than theory.

------
anon4
So this is a bit ranty and I hope it doesn't come off as too rude, but here
goes:

Let's talk about functions. Why is id . f = f . id = f? id . f is a function
which takes some argument, pushes it on the stack, calls f, pushes the result
from f on the stack and calls id. This is two function calls and you go two
stack levels deep. Just f is one function call and one stack level deep. If f
and id are real functions executing on real hardware, then id . f is not
strictly equal to f due to unavoidable side effects even if id and f are both
pure functions, e.g. you may have not enough stack space to execute id . f,
but you could execute just f. Real functions executing on real hardware aren't
exactly the same like the pure mathematical notion of a function. If Haskell
is a language for writing computer programs executing on actual computers,
then id . f ≠ f; and if it isn't, then it's not very useful.

Abstractions are well and good, but I must be able to reason how the machine
will work while executing my program. Take the official Python implementation
- CPython for example. It's nowhere near as fast as C if you write your code
naively, but there is very little surprising about it. Or take C. If you write
e.g.

    
    
        int id(int x) { return x; }
        int f(int x) { return 2*x; }
        int id_f(int x) { return id(f(x)); }
    

Then the compiler is _free_ to compile id to a noop and id_f to be exactly the
same as f, but it is not _required_. Most people won't write a program which
requires that behaviour by the compiler in order to work correctly.

If the Haskell compiler is _required_ to perform such optimisations, then I
would concede id . f = f; just like tail recursion optimisation is _required_
in Scheme, so tail recursion is actually equivalent to loops, rather than
being just mathematically equivalent.

The broader point I want to make is that a computer programming language is
inescapably tied to the reality of how hardware executes instructions and one
must have some guarantees on how what is written will translate to machine
execution.

Another thing is that once you reach a certain level of abstraction, you lose
your ability to reason about the concrete things in that class. I'll
illustrate what I mean.

Let's construct the set of Handleds. A handled is a thing that has a handle,
and a handle is a cylindrical protrusion affixed to the object, that can be
grasped by an average adult human hand and pushed or pulled along an axis
perpendicular to the cylinder's height. So, a TNT detonation device is a
handled, since you have a handle on the plunger you press to detonate the TNT.
A door also has a handle. Your laptop bag has a handle. A table leg also
technically counts as a handle and you can use it to move the table. The thing
is, while you would have a theory on how to make handles, such that they would
be easily operated by people, the fact that an object has a handle tells you
nothing of the object, other than it can be manipulated with a hand.

Pretty much the same goes for a category. Yes, you can compose two elements of
the category into a third. So... what does it do? You don't know. The problem
is, the two lumps of fat that sit in your skull need to affix some concrete
properties on the object in order to think about it. Functions are series of
instructions that transform values. Or maybe they're relations, if you're a
mathematician. But the essence of a function isn't in its composability (at
least for programmers), it's in its usage as a unit of execution. Maybe also
in its usage as a way to organise your source code.

There's no point in explaining something as "it's a category" any more than in
a car salesman starting out by pointing all the handles on the car and how the
bumpers can be grabbed as ersatz handles in case you need to get a handle on
the situation. Saying something is a category is a deferred meaning that you
can only understand once you know what that something is, so it's more of a
mental burden than an explanation of anything.

Category properties may be simple, but their relation to the actual execution
of a program is anything but. If you want to explain Haskell, maybe start off
with the simple parts. Like how what you write executes on the actual machine
and work your way up.

~~~
mbrock
Your point about equating f . id = id is related to what the equality sign
means.

Presumably you don't object to the notion that sin(a + b) = sin(a) cos(b) +
cos(a) sin(b), even though the left and right hand sides are totally different
in terms of computation.

That's because you know that such identities, while indeed abstracting over
computational details, have real meaning and are extremely useful in
mathematical thought (and algorithm design; you can use that equation to
implement an efficient numeric oscillator).

Given that you probably accept that trigonometric equality, you can't really
dismiss equational reasoning in software as useless just because it abstracts
over stack frames and machine instructions. That kind of abstraction is
exactly the point.

