Functional Programming
Notable People
Aris Goudouras
Pandoc creator & maintainer
Aris Goudouras
Co-authored the https://haskellbook.com/ book.
Aris Goudouras
fp-ts creator & maintainer
Aris Goudouras
Wadler was a principal designer of Haskell, currently works with Agda and has written a textbook for it. Came up with type classes and monadic IO.
Books
Haskell Programming from First Principles - Allen & MoronukiPDF
Graham Hutton - Programming in Haskell (2018, Cambridge University Press)PDF
The Dao of Functional Programming (Last Updated 09/01/2025) - MilewskiPDF
Monoid
Commutativity
Associativity
Appending as condensing a set of values to a summary value
Abelian or Commutative Monoid
Could be interesting for CRDT implementations
CRDTs are, in the basic case, a idempotent commutative monoid, aka an idempotent abelian monoid. https://news.ycombinator.com/item?id=7891598#:~:text=CRDTs%20are%2C%20in%20the%20basic%20case%2C%20a%20idempotent%20commutative%20monoid%2C%20aka%20an%20idempotent%20abelian%20monoid.
Folding & Foldable
Folding and Monoid
Folding in lists and its significance
`foldr` specifying the binary associative operation explicitly
`foldr` (right fold) and `foldl` (left fold) in lists
How to write folds
Catamorphisms as the generalization of folds
How `foldl` associates
How `foldr` associates
`foldl` implementation
`foldr` implementation
The “rest of the fold” in `foldr`
Folding as a two-stage process (traversal and folding)
Example of folding usage (Source: https://hackage.haskell.org/package/recursion-schemes-5.2.3#readme)
Algebras and Laws
Algebras in Haskell
The importance of taking advantage of mathematical laws when programming
Functor
The Law of Composition in Functors
The Law of Identity in Functors
Lifting
Precise Definition of Lifting
Metaphor: Lifting a Function over a Layer of Structure
Metaphor: Lifting a Function into a Context
Natural Transformations
`Either` and `Validation`: Natural Transformations, differing in the`Applicative` Instance
`Either` and `Validation`: Natural Transformations, differing in the `Applicative` Instance
Lifting
Precise Definition of Lifting
Metaphor: Lifting a Function over a Layer of Structure
Metaphor: Lifting a Function into a Context
Applicative Functor
Applicatives as Monoidal Functors
Applicative - `pure` Function
Example: Applicative and Maybe
Example: Applicative and Lists
Monoid for Smashing Structure, Functor for Mapping Values
`liftA2` - `fmap` and `apply` working together
`Either` and `Validation`: Natural Transformations, differing in the`Applicative` Instance
`Either` and `Validation`: Natural Transformations, differing in the `Applicative` Instance
Difference in using `Applicative` or `Monad` functions in sequences
Aris Goudouras
This is probably the difference between using `apS` and `bind` in fp-ts.
Monad
Monad’s `join` (flattening structure) in action
Monad as a generalization of concat, with the ability to collapse/merge structure
Difference in using `Applicative` or `Monad` functions in sequences
Aris Goudouras
This is probably the difference between using `apS` and `bind` in fp-ts.
Binding in the context of Monads
Kleisli Composition (that accounts for structure) in Monads
`mapM` as a special case of `traverse`
Parsing: A Case Study for Monads Usage

Assuming this parsing function that parses fractions from a string:

What’s the benefit of implementing it using monads?

How does this align with the fact that the Parser constructor takes just a function?

Aris Goudouras
Note that `decimal`(the first parser in the chain) is a `Parser`, which encapsulates a function that takes a `string` as input. This aligns with what the expected input type is in our case.
Monad Transformers
Leveraging the concreteness of the known monad type as the essence of Monad Transformers
A simple monad transformer: `IdentityT`
When you are trying to explain what a monad is to a Haskell programmer, how do you explain? It’s like, well, there is the Maybe Monad. Well, okay. And there’s the List Monad. Okay. And there’s the Continuation Monad. It’s like your fingers sticking out of water, right? Each finger is separate. Where is the hand? So, the way that I finally tried to explain monad is that it’s in composition. You are trying to compose things and there is this new way of composing things that is different. It’s sort of like composing functions, right? But these are not simple functions. These are functions that return some funny stuff, right? And how do you compose functions that return funny stuff? They accept normal stuff, but they return something that’s weird. […] There is something miraculous that happens with monads. The question is like, how come that all these weird effects that you can think of, they have all the explanations in terms of functions returning some functor? Why is it so? Whether it’s IO or maybe or list. Bartosz Milewski, https://haskell.foundation/podcast/30/, 32:07
Effects and I/O
Monad’s `join` (flattening structure) in action
Ordering effects by nesting lambdas
It is not possible to perform I/O outside of the IO monad – your code will not compile – and, furthermore, I/O “colours” all the functions which call it, because if you are calling something which returns IO, then you have to be returning IO as well. This might look like a lot of bureaucracy, but it actually does two very helpful things: firstly, it immediately tells the programmer “hey, this function performs side-effects in I/O” which means that they don’t have to read the code in order to understand what it does, just the type signature; secondly, it means that you cannot accidentally perform I/O in a function you thought was pure – this, in itself, eliminates whole classes of bugs in which one may think one understands all the dependencies of a function, but actually something is affecting it which you did not realise, because it can perform side-effects. - https://www.gtf.io/musings/why-haskell
Traversable
`sequenceA`: flipping layers of structure around
`mapM` as a special case of `traverse`
When to use `Traversable`
Reader
`fmap` over functions as function composition
`fmap` in partially applied functions
- Computation type: Computations which read values from a shared environment. - Binding strategy: Monad values are functions from the environment to a value. The bound function is applied to the bound value, and both have access to the shared environment. - Useful for: Maintaining variable bindings, or other shared environment. https://wiki.haskell.org/All_About_Monads#The_Reader_monad
Functor, Applicative and Monad instances for `Reader`
State
- Computation type: Computations which maintain state. - Binding strategy: Binding threads a state parameter through the sequence of bound functions so that the same state value is never used twice, giving the illusion of in-place update. - Useful for: Building computations from sequences of operations that require a shared state. https://wiki.haskell.org/All_About_Monads#The_State_monad
How the `State` monad’s bind operation threads the state parameter through the sequence of functions
MonadState functions (`get` and `put`)
Haskell
Parsing
Parser Combinators
Parser and State
End of File (`eof`)
Parsing: A Case Study for Monads Usage
JSON and ASTs
Domain Driven Design and Functional Programming
Using “Smart” Constructors to Ensure Validation Rules are Applied
Avoid Domain Events within a Bounded Context (FP + DDD)
Invariants and how to express them formally in the Type System
Example of Capturing Business Rules with the Type System
Modeling Business Workflows as Pipelines
Modeling Dependencies (FP + DDD)
Selectively Passing Functions that are Needed as Dependencies
When to Make Dependencies Explicit
Functional Programming - Paradigm & Mental Models
Why Functional Programming
We can generalise this idea of being forced to handle the failure cases by saying that Haskell makes us write total functions rather than partial functions. This means that we have to handle the entire input domain rather than only part of the input domain, otherwise the compiler will complain at us and, sometimes, point-blank refuse to give us a programme. https://www.gtf.io/musings/why-haskell
FP and Dependency Rejection
Aris Goudouras
Lists advantages in the end of this video. Effect polymorphism is a powerful idea.
Declarative vs Imperative / Global vs Local / Denotational vs Operational Approach in Problem Solving
The way I understand functional/reactive programming is that it treats all input as it were pre-existing. So, instead of thinking of: okay, now the user moved the mouse or clicked or pressed a key and we have to react to it and have handlers for it and so on, we think like this: okay, here’s a list of all actions that the user will do, so it sort of says “I know everything about the universe, I know all the future”, this is the list of mouse clicks from now till the program terminates, this is the list of keys that the user will press, okay, here is a list of input from the network that will come, okay? Here, go and calculate something for me. And you just go and take this list and say: okay, I will apply a function to a list like `fmap`, or I can do a fold on this list, or I can merge two lists, stuff like this - you just work on the whole list as if it existed. Of course it doesn’t exist at the moment the program starts running but it doesn’t matter. It’s like, if you tell it ‘’do something for every digit of pi”, does it have every digit of pi? […] it doesn’t have everything at the same time. […] in a lazy language like Haskell we don’t care that we don’t have the whole list. Conceptually we do have the whole list. When time comes to get the next element, well maybe we’ll block / the system will block waiting for user input, but we don’t care: we just have a list and operate on the whole list […]. Bartosz Milewski, https://youtu.be/3XTQSx1A3x8?si=VXcQVdOJ6koJ16oa&t=1904
Don’t think about how things are implemented in hardware. What computers are is just one of the many models of computation. We shouldn’t get attached to it. You can perform computations in your mind, or with pen and paper. The physical substrate is irrelevant to the idea of programming. Milewski, The Dao of Functional Programming, p. 1
[…] when we write code in what now I’m calling programming only language – okay, what we’ll call programming language, I’m now calling a programming-only language, meaning it can only talk about the surface and only talk about the implementation, so it misses the heart of the matter and the reusability. I think that the purpose of my code is to take some data and transform it into other data. That’s, of course, a functional perspective, but even when you’re doing imperative programming, you’re still doing that just in a sequential way. Start with some data, you end up with some data. But I don’t think that’s really what computation is about. I think that’s what the surface layer is about. I think that’s what the executable is about. But the whole process of why am I generating this computation and why am I happy with it, or should I be happy with it, and if so, why, that is not about transforming. The purposeful computation, or meaningful computation, is not about transforming data into other data. It’s about transforming meaningful data into other meaningful data and doing it meaningfully. And what I mean by that is that when I write code to do, say, binary addition, there’s a reason for that code. It is not just because I have a vector of bits and a vector of bits. It’s because I have a particular interpretation of that vector of bits. I have a particular way in which I’m interpreting that vector of bits as a natural number. So my job in constructing computation is not to transform data to data. It’s to transform, in a way, an interpretation. So there’s a type of data, and there’s a type of its meaning, and then there’s a function from the formative ladder, from the representations to the meanings. That’s the domain. That’s what I’m transforming. That’s what I’m taking in, is meaningful data, is the data type interpreted in a particular way. And that’s what my computation is generating. That’s the whole thing. That’s the higher-dimensional thing that has the purposefulness in it and has the ability to then have the logical proof in it as well. When I’m just reflecting, I look back, and when I’m programming, of course, I have an interpretation in mind. Everybody has an interpretation of mine, but what often happens is maybe I’m working with you and we have different interpretations in mind and our programming language cannot capture either one of those. And so it cannot notice the difference. You write your code. I write my code. They’re both correct. We put them together. It’s not correct. Why not? Because we didn’t say what we meant. We said, “I’m transferring bit vectors to bit vectors. […] So our programs miss their purpose, and the languages cannot capture that purpose, and therefore they cannot support us in fulfilling it. Especially we, who like typed languages, for instance, appreciate that the program is helping us, supporting us in fulfilling our intentions. https://haskell.foundation/podcast/62/ (0:52:06)
[…] there's a mental bottleneck which is the von Neumann paradigm. The von Neumann style in the in the title of this during award lecture is exactly this. And it forces to think small. We cannot think clearly and profoundly and deeply and gain insight within the von Neumann model of programming. The von Neumann programming is still the dominant model of programming. Operational semantics, which is mainly what PL people are taught is essentially mirrors this weakness. it's an essentially sequential and mechanistic way of thinking which is not rooted in beauty. It is sort of fundamentally sequential, whereas computation isn't. If if you think it is, it's because you've been programmed right with this deep misunderstanding. […] So anyway, this turning a word lecture for me is really about breaking out of the Von Neumann bottleneck, the spiritual, mental, cognitive, right, philosophical bottleneck, and and and getting back into this programming. https://open.spotify.com/episode/4jPTxGfaUa84IqsQceKywL (1:04:55)
There's one meta idea that applies, I think, to everything everybody should ever do. Certainly applies to everything I do, which is you should understand what you're talking about in the simplest, most elegant compositional terms. That is denotational semantics. That's what I call giving, having a denotation. So all your types have a mathematical model. It's simple and of course it has to be precise. Otherwise simplicity is an illusion. OK, so it's as simple as it can possibly be. And then every operation that you provide can be fully explained in terms of that model and that model has nothing to do with the implementation at all. OK, so so it it it it's a very severe discipline that forces you to to think in the clearest well, it encourages you to think in the clearest possible way about what you're trying to say, independent of any bias about implementation. That's very hard for people who have been conditioned to think that programming is about making machines do stuff. Because any anything. You know, if you think computation and if you've been taught operational semantics and operational thinking, and like, like linear algebra is about arrays and machine learning is about neural Nets, all these like arbitrary bad choices about particular representation, or programming is about sequences of effects, all these harmful ways to look at things. https://open.spotify.com/episode/4jPTxGfaUa84IqsQceKywL?context=spotify%3Ashow%3A1gE06aXNjm4BEcwbsow0Zu (1:56:43)
Yeah, so so I thought I got to think about 3D graphics in a a functional way, a compositional way. A way of what it means, not how it not what it does. OK. And that's the difference. The graphic systems are almost always implemented the way almost everything else is implemented. But especially graphics it seems is is if they are a graphics program is a sequence of commands, maybe structured, you know sequence of commands, the words like you might have loops and subroutines, what they used to call them, right. So procedure definition, so on. But they're essentially just ways of of organizing sequences of instructions that have side effects, right? But instead of the system memory, they're going to be the video memory, right? And the pattern that we want in memory after we're done as an emergent effect is one that looks like something to you like some geometry or something like that. All right. But that model is a terrible model if what you care about is clarity. Because it's extremely hard to reason about in exactly the way John [Backus] has said that that because of imperative programs, that is programs that are about sequences of effects and which have no nice mathematical properties, therefore they cannot give you any powerful insights. So what I did instead is: I said OK, what are graphics programs for? In other words, what are the meanings they are trying to convey? So I want to make a graphics program. It's because I want to help somebody look through a tool at something they are interested in. So I'm trying to invent a telescope for something. What is that something? And that's the important thing. And the secondary thing is how can I make a device that helps you look through it? How can I use a computer? How can I make a device you can look through to see what you want to see and then how can I make a language describe what you want to see? That's very different from the dominant design paradigm, which is how can I make a language or a library so that you can describe a sequence of effects right, that emerge that have this, you know eventually, you know that cause a billion effects whose whose overall aggregate result is to draw a picture, right? Because it because it messes up these two things. The essence of what it is that the the idea you're trying to describe and give somebody a a a opportunity to look at and then how the machine works and what are the kind of physics in a way what are the effects. So I divided those two things and instead of a language of rendering or even of affecting a data structure that would get rendered I designed a language with a library of vocabulary, typed, composable vocabulary of geometry and colors. I mean it's like essentially what Euclid did within a modern a more modern vocabulary, a more modern linguistic framework, right? So a collection of types and typed operations on three-dimensional things. […] https://open.spotify.com/episode/4jPTxGfaUa84IqsQceKywL?si=168fed48e7fe47fd (1:32:19)
Denotational Design Principles
Aris Goudouras
Milewski makes an interesting association between the declarative vs imperative way of thinking and the global vs local way of attacking problems in physics, as described by Feynman.
Aris Goudouras
Conal Elliott refers to a John Backus lecture (his Turing award lecture and the corresponding paper) where we renounces imperative (Von Neumann) style programming and argues for functional programming (or something close to it), although he received the award because of his work in Fortran - an imperative language. Elliott also says that Von Neumann’s style was mean to be a shortcut / experiment for pragmatic reasons and then we got stuck with it as the dominant paradigm. (starts around 54:28)
Aris Goudouras
Interesting discussion on programming paradigms.
Mental models and thinking in Functional Programming
There is one area in which knowing something about category theory actually changes the way you program in Haskell. I think that’s once you learn about algebras and coalgebras, because that gives you this level of abstraction in which you stop doing things using recursion. The first big problem when people learn Haskell or other functional language is recursion, right? Especially when you come from C++ or traditional languages, right? You always write loops, right? And you think in terms of loops, I have to iterate, iterate, right? How do I iterate in Haskell? Well, you do recursion, but recursion is kind of like a weird way of thinking when you have to sort of assume that you already solved a problem and then you have to do one more step to finish it. And it’s not natural. In iteration, you just do one step at a time, and then next step, and then next step, and then step. In recursion, you have to assume that you’ve done it already, right? I haven’t done it yet, but let’s assume that I’ve done it and now I need one more final step, right? So this is hard. But then when you learn about algebras and things like catamorphisms, anamorphisms, all this stuff, you suddenly think, “Oh, I don’t have to think recursively. I can think holistically.” It’s a data structure. I’m looking at this whole data structure and I’m applying a transformation to this whole data structure all at once. And this is so powerful. Bartosz Milewski, https://haskell.foundation/podcast/30/, 27:14
However, much in the same way that music is not a set of instruments, functional programming is not a set of abstractions that we need to learn and memorize. Functional programming is an approach to solving computational problems. Many of the abstractions that you do read about are ways to apply this approach to new problems, or problems that were difficult to solve without reaching for more traditional programming methods. But the essence, the core, of what functional programming is about is mostly unchanged. In the preface to the book Burge writes (emphasis mine): "The main emphasis [of this book] is placed on those parts of the language, namely expressions, that denote the end results sought from the computer, rather than on the instructions which the machine must follow in order to achieve the results. The main thesis of this book is that, in many cases, this emphasis on expressions as opposed to mechanisms simplifies and improves the task of programming.” The above will be familiar to anyone who has had the good fortune of taking a well designed functional programming course. This is the paradigm shift that sometimes makes functional programming hard to learn for those that are used to other methods of programming. Expressions over mechanisms. Jose Calderon, http://jmct.cc/intro/index.html
Even if you are using functions/methods that are usually met in the functional programming paradigm, if you think about the implementation details (e.g. mentally iterating over arrays or worrying about how map executes under the hood), you are in the imperative mindset. With functional programming and a declarative mindset, the focus should be on the intent of the operation applied.
Aris Goudouras
Importantly, it’s not that in functional programming we don’t deal with mechanisms or low-level constructs. It’s the emphasis that changes in how we attack the problem (from the instructions to the end results) that makes the difference.
Aris Goudouras
Interestingly, Burge’s and Calderon’s perspective is much different than Milewski’s, who focuses on the abstractions and category theory part.
Aris Goudouras
I think Burge (and Calderon) here endorse the declarative style manifested in the expressions. But I don’t really understand at this point why FP expressions are declarative in FP (especially with ones like Do notation, we really have the opposite).
Aris Goudouras
Haven’t read this yet but seems relevant
Folding & Foldable
Folding and Monoid
Folding in lists and its significance
`foldr` specifying the binary associative operation explicitly
`foldr` (right fold) and `foldl` (left fold) in lists
How to write folds
Catamorphisms as the generalization of folds
How `foldl` associates
How `foldr` associates
`foldl` implementation
`foldr` implementation
The “rest of the fold” in `foldr`
Folding as a two-stage process (traversal and folding)
Example of folding usage (Source: https://hackage.haskell.org/package/recursion-schemes-5.2.3#readme)
Pattern-Matching
Prefer non-overlapping patterns
Category Theory and Functional Programming
Objects and Elements
Category Theory and Structure
So what is category theory about? In every other aspect, either mathematics or physics or programming, we always look at the structure of things, right? In order to understand something, we want to decompose it and find what’s inside, right? It’s like you get a watch and you want to see like, how does it work? Why is it ticking, right? So you open it, you find the little parts, and then you decompose it into tiny little bits. Maybe you decompose it into atoms and then quartz, depending on how far you want to get to understand it. And category theory is very different from this. In category theory, you say I want to learn about this object, but I’m not going to look inside of this object at all. This object is black box. It’s a point. It has no structure. The structure of this object is given by how it interacts with other similar objects. So you always have a whole category of things. You want to talk about groups? Okay, what’s a category of groups? And in the category of groups, you don’t talk about what a group is like, what’s inside the group. No, you say, how is this group related to other groups, right? It’s more about understanding structure. I mean, it’s an amazing thing what category theory does, that it says, “Here’s a structure, it’s just objects and arrows. That’s it.” There’s nothing else. There’s just objects and arrows. And then you can study it and you can discover that these arrows can move in weird ways and combine in such crazy ways. And if you want to describe objects using just arrows, because arrows tell you how they interact with other objects, right? So there is a completely new way of talking about structure by saying how the arrows arrange themselves, right? And there is this stuff, for instance, called universal construction. Most of the things in category theory are defined using universal construction. A universal construction says, in order to understand this object, you have to tell me how it interacts with the whole universe, with every other object, not just a few objects, okay? No, with every object. That’s the universality of it, right? So this is so philosophical. You can’t be more philosophical than that, right? Make me whole with the universe, right? Bartosz Milewski, https://haskell.foundation/podcast/30/, 38:32
A set as a category with no structure
Composition, Application and Identity
Identity
Function application as function composition
Associativity of composition and its importance to problem-solving
Composition and problem-solving
Definition of function/morphism composition
Expression of composition in Haskell, where types and functions form a Category
Category Theory and other Mathematical Theories
Category theory as complementary to set theory
A set as a category with no structure
Category of Sets (Category of Types and Functions)
Category Theory and Thought Processes
Abstraction as discarding information in a function
Aris Goudouras
Not all functions perform abstraction (Milewski gives an example right after of injective functions / monomorphisms, which don’t abstract).
Morhpism/Function Types
Injective Functions / Monomorphisms
Surjective Functions / Epimorphisms
Bijective Functions / Isomorphisms
Types
Sum Types
Introduction, elimination and computation rules
Product Types
Function Types
Functors
Functorial data types
Type constructors related to container & parameterized types
Important: type constructors not to be confused with arrows between objects
Functor as a kind of mapping that preserves structure
Endofunctors: functors that map a category to itself
Functoriality in Haskell (Functor type class and `fmap`)
Functoriality & Lifting
Type constructors: mapping types to other types
Composition and identity preservation with functors left to the programmer in Haskell
Types of Functors
Important Categories for Programming
Category of Sets (Category of Types and Functions)
Opposite categories
Product categories of interest to programming
Endofunctors: functors that map a category to itself
Natural Transformations
Algebras
Goal: A general procedure for generating and manipulating arbitrary recursive types from simpler pluggable components
Recursion Schemes, Algebras and Coalgebras
Catamorphisms in recursion schemes
The use of functors in recursion scheme catamorphisms and anamorphisms
Anamorphisms in recursion schemes
Functors and recursion schemes
The use of functors in recursion scheme catamorphisms and anamorphisms
Idea & intuition behind recursion schemes
Functional programming is all about functions, right? Using a recursive function is the correct approach. Or is it? The more you program in Haskell, the more you realize that you get the most power by considering wholesale transformations of data structures. When creating a Map of pieces, I didn’t write a recursive function over a list — I used a fold instead. Of course, behind the scenes, fold is implemented using recursion (which, thanks to tail recursion, is usually transformed into a loop). But the idea of applying transformations to data structures is what lets us soar above the sea of details and into the higher levels of abstraction. Bartosz Milewski, https://bartoszmilewski.com/2017/12/29/stalking-a-hylomorphism-in-the-wild/
Many recursive functions share the same structure, e.g. pattern-match on the input and, depending on the data constructor, either recur on a smaller input or terminate the recursion with the base case. Another one: start with a seed value, use it to produce the first element of an infinite list, and recur on a modified seed in order to produce the rest of the list. Such a structure is called a recursion scheme. Using higher-order functions to implement those recursion schemes makes your code clearer, faster, and safer. https://hackage.haskell.org/package/recursion-schemes-5.2.3#readme
Important: Separating the how from the what using recursion schemes

Definitions & Tutorials

Discussion: What are good use cases, if any?

Implementation / Libraries

Agda