Hereās a probably imprecise understanding of āfree monadā (or free structures in general) that Iām still shaping. I should disclaim I donāt really get the math, my intuition is rooted in Haskell, and so code examples and explanations will use Haskell.
Free ⦠as in beer?
Why is it called a āfreeā monad? Is it, āfree as in beerā? āFree as in speechā? āFree as in [some other thing]ā?
To answer this, I think itās helpful to look at some other āfreeā
things. Haskellers are also known to say, āList is the free
Monoidā. Can we draw some generalizations from these two
examples? List and Free are type constructors,
Monoid and Monad are type classes. So Iām
going to assert this: when a Haskeller says, āFoo
is the free Bloopā, they are saying the
following:
Foois a type constructor (meaning, it takes some other type as input, and returns a type). Specifically,Foohas kindinkind ā outkindfor some kindsinkind&outkind.Bloopis a type class for types of kindoutkind(the kind that you get from partially-applyingFoo)- You can write a
Bloopinstance forFooapplied to any input type of kindinkind(without knowing anything about that input type)
In āList is the free
Monoidā. List is a type constructor,
and I can write a Monoid instance for List a
for any type a, without knowing anything about
a. In other words, the Monoid instance doesnāt
use anything specific to a, it just uses List
operations.
instance Monoid (List item) where
(<>) = (++)
mempty = []What are some other ones?
NonEmptyListis the freeSemigroupfor similar reasonsMaybeis the freeDefaultbecause I can choosedef = Nothingfor any typeCompose Set Listis the freeSemiring
So I think of this as āfree as in beerā: these are instances you get
āfor freeā just by putting a into the structure of the type
constructor, you donāt need any further constraints on the
a.
Note that āFree is the free Monadā is an
outlier in a few ways:
- the
inkindkind isType ā Type(the other examples haveinkind ā· Type)āi.e.Ā itās a type constructor that takes a type constructor - āfreeā is the name of the data type, but really itās not any more free than the other types listed aboveāthis seems like plain bad naming to me
- in fact, itās not even quite as free as the othersāit
requires a
Functorconstraint on its input type (more on this below)
Why though
This doesnāt really satisfy the question of why you might want to do thisāwhat are you getting for free by using any of these free structures?
Hereās a dumb but maybe illustrative example. Say you regularly play Super Smash Bros (the N64 game) with your friends. You keep track of whoās the best player by who has had the most KOs in a game. Maybe you write this down on a white board. As soon as someone scores higher than the current champion, you clear the board and write the new championās name and score.
As more games are played, thereās a tie for highest score. To break the tie, youād like to change the criteria for best player: instead of all-time highest KOs, it should be whoever has won the most games total. Too bad! Nobody remembers how many games each player has won, all youāve kept track of is KOs.
What you could have done instead, is kept a log of all the scores of
each game (a List Results, where Results
contains the game results: place/ranking and KOs per player). That way
you can calculate the best player afterwards, using whatever criteria
youād like.
Calculating best player in different ways is like plugging in a
different Monoid in a foldMap over the
List Result log.
foldMap
:: Monoid m
=> (a -> m) -- this lets you pick the criteria
-> [a] -- your logs
-> m -- your result (best player)If you use a function that turns Results into
(Max Int, First Player) as your (a -> m)
thatās your max KOs, if you use
Results -> Map Player (Sum Int) that would show you who
has won the most games, etc.
Thereās a similar thing in Free called
foldFree
foldFree
:: Monad m
=> (forall x. f x -> m x) -- this lets you pick how your AST is interpreted
-> Free f a -- your AST
-> m a -- your resultAgain, the naming kinda gets in the way: foldFree seems
to be doing just as much āmappingā as foldMap, and as we
saw both Free and [] are āfreeā.
Aside: Hm, maybe this is āfree as in speechā after all?
Freer
Remember when we said that Free was an outlier because
it required a Functor constraint on f? Turns
out you can get fās Functor instance āfor
freeā (I think?) using something called Coyoneda. I donāt
yet understand what this is about, but apparently itās in this paper:
https://okmij.org/ftp/Haskell/extensible/more.pdf