Types à la Curry in Simply Typed Lamba Calculus - haskell

I'm writing a toy theorem prover with Haskell following the model of L.Paulson; one of the creators of Isabelle.
According to one of his articles, a theorem prover may be built with the Simply Typed Lambda Calculus. I created the data type with
data TLTerms = Var Id
| Const Id Types
| Lambda Id Types TLTerms
| Comp TLTerms TLTerms
deriving (Eq)
i.e. following types à la Church. However, I would like to implement the type system à la Curry. Maybe it is not the best of the systems for this purpose; but I want to try the Curry-style in order to judge which style is better. The first problem is type inference.
checkType :: TypeContext -> TLTerms -> Maybe Types
-- Given a context, if i is in the context
-- i has its type assigned in the context
checkType ls (Var i) = case lookup i ls of
Just t -> Just t
Nothing -> Nothing
-- the constants come predefined by the alphabet
checkType _ (Const _ typ) = Just typ
-- the abstractions have type sigma -> tau
checkType ls (Lambda x typ terms) = do
t <- checkType ((x,typ): ls) terms
return $ Arrow typ t
checkType ls (Comp term1 term2) = do
x <- checkType ls term1
y <- checkType ls term2
case (x,y) of
(Arrow a b, c) -> if a==c then Just b else Nothing
_ -> Nothing
I took the code from another related question. I surely believe there's a more elegant algorithm to verify if a lambda-term is well-typed. My questions are:
which parts should be changed/added/removed (apart from deleting the
type argument in abstractions);
which error monad should be better:
Maybe or Either or another (I want to return a value which means
failure);
or if I should create another function from scratch.
PD: I'm new to Haskell, although I've written example programs like dual numbers or power series. Just to explain my level of knowledge in this subject.

Related

Parsing to Free Monads

Say I have the following free monad:
data ExampleF a
= Foo Int a
| Bar String (Int -> a)
deriving Functor
type Example = Free ExampleF -- this is the free monad want to discuss
I know how I can work with this monad, eg. I could write some nice helpers:
foo :: Int -> Example ()
foo i = liftF $ Foo i ()
bar :: String -> Example Int
bar s = liftF $ Bar s id
So I can write programs in haskell like:
fooThenBar :: Example Int
fooThenBar =
do
foo 10
bar "nice"
I know how to print it, interpret it, etc. But what about parsing it?
Would it be possible to write a parser that could parse arbitrary
programs like:
foo 12
bar nice
foo 11
foo 42
So I can store them, serialize them, use them in cli programs etc.
The problem I keep running into is that the type of the program depends on which program is being parsed. If the program ends with a foo it's of
type Example () if it ends with a bar it's of type Example Int.
I do not feel like writing parsers for every possible permutation (it's simple here because there are only two possibilities, but imagine we add
Baz Int (String -> a), Doo (Int -> a), Moz Int a, Foz String a, .... This get's tedious and error-prone).
Perhaps I'm solving the wrong problem?
Boilerplate
To run the above examples, you need to add this to the beginning of the file:
{-# LANGUAGE DeriveFunctor #-}
import Control.Monad.Free
import Text.ParserCombinators.Parsec
Note: I put up a gist containing this code.
Not every Example value can be represented on the page without reimplementing some portion of Haskell. For example, return putStrLn has a type of Example (String -> IO ()), but I don't think it makes sense to attempt to parse that sort of Example value out of a file.
So let's restrict ourselves to parsing the examples you've given, which consist only of calls to foo and bar sequenced with >> (that is, no variable bindings and no arbitrary computations)*. The Backus-Naur form for our grammar looks approximately like this:
<program> ::= "" | <expr> "\n" <program>
<expr> ::= "foo " <integer> | "bar " <string>
It's straightforward enough to parse our two types of expression...
type Parser = Parsec String ()
int :: Parser Int
int = fmap read (many1 digit)
parseFoo :: Parser (Example ())
parseFoo = string "foo " *> fmap foo int
parseBar :: Parser (Example Int)
parseBar = string "bar " *> fmap bar (many1 alphaNum)
... but how can we give a type to the composition of these two parsers?
parseExpr :: Parser (Example ???)
parseExpr = parseFoo <|> parseBar
parseFoo and parseBar have different types, so we can't compose them with <|> :: Alternative f => f a -> f a -> f a. Moreover, there's no way to know ahead of time which type the program we're given will be: as you point out, the type of the parsed program depends on the value of the input string. "Types depending on values" is called dependent types; Haskell doesn't feature a proper dependent type system, but it comes close enough for us to have a stab at making this example work.
Let's start by forcing the expressions on either side of <|> to have the same type. This involves erasing Example's type parameter using existential quantification.†
data Ex a = forall i. Wrap (a i)
parseExpr :: Parser (Ex Example)
parseExpr = fmap Wrap parseFoo <|> fmap Wrap parseBar
This typechecks, but the parser now returns an Example containing a value of an unknown type. A value of unknown type is of course useless - but we do know something about Example's parameter: it must be either () or Int because those are the return types of parseFoo and parseBar. Programming is about getting knowledge out of your brain and onto the page, so we're going to wrap up the Example value with a bit of GADT evidence which, when unwrapped, will tell you whether a was Int or ().
data Ty a where
IntTy :: Ty Int
UnitTy :: Ty ()
data (a :*: b) i = a i :&: b i
type Sig a b = Ex (a :*: b)
pattern Sig x y = Wrap (x :&: y)
parseExpr :: Parser (Sig Ty Example)
parseExpr = fmap (\x -> Sig UnitTy x) parseFoo <|>
fmap (\x -> Sig IntTy x) parseBar
Ty is (something like) a runtime "singleton" representative of Example's type parameter. When you pattern match on IntTy, you learn that a ~ Int; when you pattern match on UnitTy you learn that a ~ (). (Information can be made to flow the other way, from types to values, using classes.) :*:, the functor product, pairs up two type constructors ensuring that their parameters are equal; thus, pattern matching on the Ty tells you about its accompanying Example.
Sig is therefore called a dependent pair or sigma type - the type of the second component of the pair depends on the value of the first. This is a common technique: when you erase a type parameter by existential quantification, it usually pays to make it recoverable by bundling up a runtime representative of that parameter.
Note that this use of Sig is equivalent to Either (Example Int) (Example ()) - a sigma type is a sum, after all - but this version scales better when you're summing over a large (or possibly infinite) set.
Now it's easy to build our expression parser into a program parser. We just have to repeatedly apply the expression parser, and then manipulate the dependent pairs in the list.
parseProgram :: Parser (Sig Ty Example)
parseProgram = fmap (foldr1 combine) $ parseExpr `sepBy1` (char '\n')
where combine (Sig _ val) (Sig ty acc) = Sig ty (val >> acc)
The code I've shown you is not exemplary. It doesn't separate the concerns of parsing and typechecking. In production code I would modularise this design by first parsing the data into an untyped syntax tree - a separate data type which doesn't enforce the typing invariant - then transform that into a typed version by type-checking it. The dependent pair technique would still be necessary to give a type to the output of the type-checker, but it wouldn't be tangled up in the parser.
*If binding is not a requirement, have you thought about using a free applicative to represent your data?
†Ex and :*: are reusable bits of machinery which I lifted from the Hasochism paper
So, I worry that this is the same sort of premature abstraction that you see in object-oriented languages, getting in the way of things. For example, I am not 100% sure that you are using the structure of the free monad -- your helpers for example simply seem to use id and () in a rather boring way, in fact I'm not sure if your Int -> x is ever anything other than either Pure :: Int -> Free ExampleF Int or const (something :: Free ExampleF Int).
The free monad for a functor F can basically be described as a tree whose data is stored in leaves and whose branching factor is controlled by the recursion in each constructor of the functor F. So for example Free Identity has no branching, hence only one leaf, and thus has the same structure as the monad:
data MonoidalFree m x = MF m x deriving (Functor)
instance Monoid m => Monad (MonoidalFree m) where
return x = MF mempty x
MF m x >>= my_x = case my_x x of MF n y -> MF (mappend m n) y
In fact Free Identity is isomorphic to MonoidalFree (Sum Integer), the difference is just that instead of MF (Sum 3) "Hello" you see Free . Identity . Free . Identity . Free . Identity $ Pure "Hello" as the means of tracking this integer. On the other hand if you have data E x = L x | R x deriving (Functor) then you get a sort of "path" of Ls and Rs before you hit this one leaf, Free E is going to be isomorphic to MonoidalFree [Bool].
The reason I'm going through this is that when you combine Free with an Integer -> x functor, you get an infinitely branching tree, and when I'm looking through your code to figure out how you're actually using this tree, all I see is that you use the id function with it. As far as I can tell, that restricts the recursion to either have the form Free (Bar "string" Pure) or else Free (Bar "string" (const subExpression)), in which case the system would seem to reduce completely to the MonoidalFree [Either Int String] monad.
(At this point I should pause to ask: Is that correct as far as you know? Was this what was intended?)
Anyway. Aside from my problems with your premature abstraction, the specific problem that you're citing with your monad (you can't tell the difference between () and Int has a bunch of really complicated solutions, but one really easy one. The really easy solution is to yield a value of type Example (Either () Int) and if you have a () you can fmap Left onto it and if you have an Int you can fmap Right onto it.
Without a much better understanding of how you're using this thing over TCP/IP we can't recommend a better structure for you than the generic free monads that you seem to be finding -- in particular we'd need to know how you're planning on using the infinite-branching of Int -> x options in practice.

Why in algebraic data types, if I can define a special `from` and `to` function for two types, the two types can be considered equality?

I'm reading this blog: http://chris-taylor.github.io/blog/2013/02/10/the-algebra-of-algebraic-data-types/
It says:
However, when I talk about equality, I don’t mean Haskell equality, in the sense of the (==) function. Instead, I mean that the two types are in one-to-one correspondence – that is, when I say that two types a and b are equal, I mean that you could write two functions
from :: a -> b
to :: b -> a
that pair up values of a with values of b, so that the following equations always hold (here the == is genuine, Haskell-flavored equality):
to (from a) == a
from (to b) == b
And later, there are many laws based on this definition:
Add Void a === a
Add a b === Add b a
Mul Void a === Void
Mul () a === a
Mul a b === Mul b a
I can't understand why we can safely get these laws based on the definition of "equality"? Can use use other definitions? What can we do with this definition? Does it make sense for Haskell type systems?
The term that the author is skating around, so as not to "mention category theory or advanced math", is cardinality. He defines two types to be ===-equal to each other if they have equal cardinality -- that is, if there are as many possible values of one as there are of the other.
Because if two types have equal cardinality, there exists an isomorphism between them. Mul () Bool may be a different type than Bool, but there are exactly as many members of one as the other, and one can trivially define a function to go from one to the other, or the other to the one. (Not that there is only one such isomorphism -- the point is, you could choose one.)
It's not a great approach. It works fine for finite sets, basically, but it introduces unfortunate side effects for infinite sets, like Add Int Int === Int. Still, for the basic description of addition and multiplication of types, it seems to serve.
Informally speaking, when two mathematical structures A,B have two "nice" functions from,to satifying from . to == id and to . from == id, the structures A,B are said to be isomorphic.
The actual definition of "nice" function varies with the kind of structure at hand (and sometimes, different definitions of "nice" give rise to distinct notions of isomorphism).
The idea behind isomorphic structures is that, roughly, they "work" in exactly the same way. For instance, consider a structure A made by the booleans True,False with &&,|| as operations. Let then structure B made of the two naturals1,0 with min,max as operations. These are different structures, yet they share the same rules. For instance True && x == x and 1 `min` x == x for all x. A and B are isomorphic: function to will map True to 1, and False to 0, while from will perform the opposite mapping.
(Note that while we could map True to 0 and False to 1, and we would still get from . to == id and its dual, this mapping would not be considered "nice" since it would not preserve the structure: e.g., to (True && x) == to x yet to (True && x) == to True `min` to x == 0 `min` to x == 0 .)
Another example in a different setting: consider A to be a circle in a plane, while B is a square in such plane. One can then define continuous mappings to,from between them. This can be done with any two "closed loops", loosely speaking, which can be said to be isomorphic. Instead a circle and an "eight" shape do not admit such continuous mappings: the self-intersecting point in the "eight" can not be mapped to any point in the circle in a continuous way (roughly, four "ways" depart from it, while points in the circle have only two such "ways").
In Haskell, types are similarly said to be isomorphic when two Haskell-definable functions from,to exist between them satisfying the rules above. Here being a "nice" function just means being definable in Haskell. The linked web blog shows a few such isomorphic types. Here's another example, using recursive types:
List1 a = Add Unit (Mul a (List1 a))
List2 a = Add Unit (Add a (Mul a (Mul a (List2 a))))
Intuitively, the first reads as: "a list is either the empty list, or a pair made of an element and a list". The second reads as: "a list is either the empty list, or a single element, or a triple make of an element, another element, and a list". One can convert between the two by handling the elements two at a time.
Another example:
Tree a = Add Unit (Mul a (Mul (Tree a) (Tree a)))
You can prove that the type Tree Unit is isomorphic to List1 (Tree Unit) by exploiting the algebraic laws fond in the blog. Below, = stands for isomorphism.
List1 (Tree Unit)
-- definition of List1 a
= Add Unit (Mul (Tree Unit) (List1 (Tree Unit)))
-- by inductive hypothesis, the inner `List1 (Tree Unit)` is isomorphic to `Tree Unit`
= Add Unit (Mul (Tree Unit) (Tree Unit))
-- definition of Tree a
= Tree Unit
The above proof sketch induces the function to as follows.
data Add a b = InL a | InR b
data Mul a b = P a b
type Unit = ()
newtype List1 a = List1 (Add Unit (Mul a (List1 a)))
newtype Tree a = Tree (Add Unit (Mul a (Mul (Tree a) (Tree a))))
to :: List1 (Tree Unit) -> Tree Unit
to (List1 (InL ())) = Tree (InL ())
to (List1 (InR (P t ts))) = Tree (InR (P () (P t (to ts))))
Note how recursive call plays the role the inductive hypothesis has in the proof.
Writing from is left as an exercise :-P
Why in algebraic data types, if I can define a special from and to function for two types, the two types can be considered equal?
Well, the better term to use here isn't "equal," but rather isomorphic. The thing is that when two types are isomorphic, they are basically interchangeable with each other; any program written in terms of A could, in principle, be written in terms of B instead, without changing the meaning of the program. Suppose you have:
from :: A -> B
to :: B -> A
and these two functions constitute an isomorphism, that is:
to (from a) == a
from (to b) == b
Now, if you have any function that takes A as an argument, you can for example write a counterpart that takes B as an argument instead:
foo :: B -> Something
foo = originalFoo . from
where originalFoo :: A -> Something
originalFoo a = ...
And for any function that produces an A, you can likewise do this:
bar :: Something -> B
bar = to . originalBar
where originalBar :: Something -> A
originalBar something = ...
Now you've hidden all uses of A inside the where subdefinitions. You could continue down this path and mechanically eliminate all uses of A entirely, and you're guaranteed the program will work the same as when you started.

How can quotient types help safely expose module internals?

Reading up on quotient types and their use in functional programming, I came across this post. The author mentions Data.Set as an example of a module which provides a ton of functions which need access to module's internals:
Data.Set has 36 functions, when all that are really needed to ensure the meaning of a set ("These elements are distinct") are toList and fromList.
The author's point seems to be that we need to "open up the module and break the abstraction" if we forgot some function which can be implemented efficiently only using module's internals.
He then says
We could alleviate all of this mess with quotient types.
but gives no explanation to that claim.
So my question is: how are quotient types helping here?
EDIT
I've done a bit more research and found a paper "Constructing Polymorphic Programs with Quotient Types". It elaborates on declaring quotient containers and mentions the word "efficient" in abstract and introduction. But if I haven't misread, it does not give any example of an efficient representation "hiding behind" a quotient container.
EDIT 2
A bit more is revealed in "[PDF] Programming in Homotopy Type Theory" paper in Chapter 3. The fact that quotient type can be implemented as a dependent sum is used. Views on abstract types are introduced (which look very similar to type classes to me) and some relevant Agda code is provided. Yet the chapter focuses on reasoning about abstract types, so I'm not sure how this relates to my question.
I recently made a blog post about quotient types, and I was led here by a comment. The blog post may provide some additional context in addition to the papers referenced in the question.
The answer is actually pretty straightforward. One way to arrive at it is to ask the question: why are we using an abstract data type in the first place for Data.Set?
There are two distinct and separable reasons. The first reason is to hide the internal type behind an interface so that we can substitute a completely new type in the future. The second reason is to enforce implicit invariants on values of the internal type. Quotient type and their dual subset types allow us to make the invariants explicit and enforced by the type checker so that we no longer need to hide the representation. So let me be very clear: quotient (and subset) types do not provide you with any implementation hiding. If you implement Data.Set with quotient types using lists as your representation, then later decide you want to use trees, you will need to change all code that uses your type.
Let's start with a simpler example (leftaroundabout's). Haskell has an Integer type but not a Natural type. A simple way to specify Natural as a subset type using made up syntax would be:
type Natural = { n :: Integer | n >= 0 }
We could implement this as an abstract type using a smart constructor that threw an error when given a negative Integer. This type says that only a subset of the values of type Integer are valid. Another approach we could use to implement this type is to use a quotient type:
type Natural = Integer / ~ where n ~ m = abs n == abs m
Any function h :: X -> T for some type T induces a quotient type on X quotiented by the equivalence relation x ~ y = h x == h y. Quotient types of this form are more easily encoded as abstract data types. In general, though, there may not be such a convenient function, e.g.:
type Pair a = (a, a) / ~ where (a, b) ~ (x, y) = a == x && b == y || a == y && b == x
(As to how quotient types relate to setoids, a quotient type is a setoid that enforces that you respect its equivalence relation.) This second definition of Natural has the property that there are two values that represent 2, say. Namely, 2 and -2. The quotient type aspect says we are allowed to do whatever we want with the underlying Integer, so long as we never produce a result that differentiates between these two representatives. Another way to see this is that we can encode a quotient type using subset types as:
X/~ = forall a. { f :: X -> a | forEvery (\(x, y) -> x ~ y ==> f x == f y) } -> a
Unfortunately, that forEvery is tantamount to checking equality of functions.
Zooming back out, subset types add constraints on producers of values and quotient types add constraints on consumers of values. Invariants enforced by an abstract data type may be a mixture of these. Indeed, we may decide to represent a Set as the following:
data Tree a = Empty | Branch (Tree a) a (Tree a)
type BST a = { t :: Tree a | isSorted (toList t) }
type Set a = { t :: BST a | noDuplicates (toList t) } / ~
where s ~ t = toList s == toList t
Note, nothing about this ever requires us to actually execute isSorted, noDuplicates, or toList. We "merely" need to convince the type checker that the implementations of functions on this type would satisfy these predicates. The quotient type allows us to have a redundant representation while enforcing that we treat equivalent representations in the same way. This doesn't mean we can't leverage the specific representation we have to produce a value, it just means that we must convince the type checker that we would have produced the same value given a different, equivalent representation. For example:
maximum :: Set a -> a
maximum s = exposing s as t in go t
where go Empty = error "maximum of empty Set"
go (Branch _ x Empty) = x
go (Branch _ _ r) = go r
The proof obligation for this is that the right-most element of any binary search tree with the same elements is the same. Formally, it's go t == go t' whenever toList t == toList t'. If we used a representation that guaranteed the tree would be balanced, e.g. an AVL tree, this operation would be O(log N) while converting to a list and picking the maximum from the list would be O(N). Even with this representation, this code is strictly more efficient than converting to a list and getting the maximum from the list. Note, that we could not implement a function that displayed the tree structure of the Set. Such a function would be ill-typed.
I'll give a simpler example where it's reasonably clear. Admittedly I myself don't really see how this would translate to something like Set, efficiently.
data Nat = Nat (Integer / abs)
To use this safely, we must be sure that any function Nat -> T (with some non-quotient T, for simplicity's sake) does not depend on the actual integer value, but only on its absolute. To do so, it's not really necessary to hide Integer completely; it would be sufficient to prevent you from matching on it directly. Instead, the compiler might rewrite the matches, e.g.
even' :: Nat -> Bool
even' (Nat 0) = True
even' (Nat 1) = False
even' (Nat n) = even' . Nat $ n - 2
could be rewritten to
even' (Nat n') = case abs n' of
[|abs 0|] -> True
[|abs 1|] -> False
n -> even' . Nat $ n - 2
Such a rewriting would point out equivalence violations, e.g.
bad (Nat 1) = "foo"
bad (Nat (-1)) = "bar"
bad _ = undefined
would rewrite to
bad (Nat n') = case n' of
1 -> "foo"
1 -> "bar"
_ -> undefined
which is obviously an overlapped pattern.
Disclaimer: I just read up on quotient types upon reading this question.
I think the author's just saying that sets can be described as quotient types over lists. Ie: (making up some haskell-like syntax):
data Set a = Set [a] / (sort . nub) deriving (Eq)
Ie, a Set a is just a [a] with equality between two Set a's determined by whether the sort . nub of the underlying lists are equal.
We could do this explicitly like this, I guess:
import Data.List
data Set a = Set [a] deriving (Show)
instance (Ord a, Eq a) => Eq (Set a) where
(Set xs) == (Set ys) = (sort $ nub xs) == (sort $ nub ys)
Not sure if this is actually what the author intended as this isn't a particularly efficient way of implementing a set. Someone can feel free to correct me.

How to work around F#'s type system

In Haskell, you can use unsafeCoerce to override the type system. How to do the same in F#?
For example, to implement the Y-combinator.
I'd like to offer a different solution, based on embedding the untyped lambda calculus in a typed functional language. The idea is to create a data type that allows us to change between types α and α → α, which subsequently allows to escape the restrictions of a type system. I'm not very familiar with F# so I'll give my answer in Haskell, but I believe it could be adapted easily (perhaps the only complication could be F#'s strictness).
-- | Roughly represents morphism between #a# and #a -> a#.
-- Therefore we can embed a arbitrary closed λ-term into #Any a#. Any time we
-- need to create a λ-abstraction, we just nest into one #Any# constructor.
--
-- The type parameter allows us to embed ordinary values into the type and
-- retrieve results of computations.
data Any a = Any (Any a -> a)
Note that the type parameter isn't significant for combining terms. It just allows us to embed values into our representation and extract them later. All terms of a particular type Any a can be combined freely without restrictions.
-- | Embed a value into a λ-term. If viewed as a function, it ignores its
-- input and produces the value.
embed :: a -> Any a
embed = Any . const
-- | Extract a value from a λ-term, assuming it's a valid value (otherwise it'd
-- loop forever).
extract :: Any a -> a
extract x#(Any x') = x' x
With this data type we can use it to represent arbitrary untyped lambda terms. If we want to interpret a value of Any a as a function, we just unwrap its constructor.
First let's define function application:
-- | Applies a term to another term.
($$) :: Any a -> Any a -> Any a
(Any x) $$ y = embed $ x y
And λ abstraction:
-- | Represents a lambda abstraction
l :: (Any a -> Any a) -> Any a
l x = Any $ extract . x
Now we have everything we need for creating complex λ terms. Our definitions mimic the classical λ-term syntax, all we do is using l to construct λ abstractions.
Let's define the Y combinator:
-- λf.(λx.f(xx))(λx.f(xx))
y :: Any a
y = l (\f -> let t = l (\x -> f $$ (x $$ x))
in t $$ t)
And we can use it to implement Haskell's classical fix. First we'll need to be able to embed a function of a -> a into Any a:
embed2 :: (a -> a) -> Any a
embed2 f = Any (f . extract)
Now it's straightforward to define
fix :: (a -> a) -> a
fix f = extract (y $$ embed2 f)
and subsequently a recursively defined function:
fact :: Int -> Int
fact = fix f
where
f _ 0 = 1
f r n = n * r (n - 1)
Note that in the above text there is no recursive function. The only recursion is in the Any data type, which allows us to define y (which is also defined non-recursively).
In Haskell, unsafeCoerce has the type a -> b and is generally used to assert to the compiler that the thing being coerced actually has the destination type and it's just that the type-checker doesn't know it.
Another, less common use, is to reinterpret a pattern of bits as another type. For example an unboxed Double# could be reinterpreted as an unboxed Int64#. You have to be sure about the underlying representations for this to be safe.
In F#, the first application can be achieved with box |> unbox as John Palmer said in a comment on the question. If possible use explicit type arguments to make sure that you don't accidentally have the wrong coercion inferred, e.g. box<'a> |> unbox<'b> where 'a and 'b are type variables or concrete types that are already in scope in your code.
For the second application, look at the BitConverter class for specific conversions of bit-patterns. In theory you could also do something like interfacing with unmanaged code to achieve this, but that seems very heavyweight.
These techniques won't work for implementing the Y combinator because the cast is only valid if the runtime objects actually do have the target type, but with the Y combinator you actually need to call the same function again but with a different type. For this you need the kinds of encoding tricks mentioned in the question John Palmer linked to.

What is the purpose of the reader monad?

The reader monad is so complex and seems to be useless. In an imperative language like Java or C++, there is no equivalent concept for the reader monad, if I am not mistaken.
Can you give me a simple example and clear this up a little bit?
Don't be scared! The reader monad is actually not so complicated, and has real easy-to-use utility.
There are two ways of approaching a monad: we can ask
What does the monad do? What operations is it equipped with? What is it good for?
How is the monad implemented? From where does it arise?
From the first approach, the reader monad is some abstract type
data Reader env a
such that
-- Reader is a monad
instance Monad (Reader env)
-- and we have a function to get its environment
ask :: Reader env env
-- finally, we can run a Reader
runReader :: Reader env a -> env -> a
So how do we use this? Well, the reader monad is good for passing (implicit) configuration information through a computation.
Any time you have a "constant" in a computation that you need at various points, but really you would like to be able to perform the same computation with different values, then you should use a reader monad.
Reader monads are also used to do what the OO people call dependency injection. For example, the negamax algorithm is used frequently (in highly optimized forms) to compute the value of a position in a two player game. The algorithm itself though does not care what game you are playing, except that you need to be able to determine what the "next" positions are in the game, and you need to be able to tell if the current position is a victory position.
import Control.Monad.Reader
data GameState = NotOver | FirstPlayerWin | SecondPlayerWin | Tie
data Game position
= Game {
getNext :: position -> [position],
getState :: position -> GameState
}
getNext' :: position -> Reader (Game position) [position]
getNext' position
= do game <- ask
return $ getNext game position
getState' :: position -> Reader (Game position) GameState
getState' position
= do game <- ask
return $ getState game position
negamax :: Double -> position -> Reader (Game position) Double
negamax color position
= do state <- getState' position
case state of
FirstPlayerWin -> return color
SecondPlayerWin -> return $ negate color
Tie -> return 0
NotOver -> do possible <- getNext' position
values <- mapM ((liftM negate) . negamax (negate color)) possible
return $ maximum values
This will then work with any finite, deterministic, two player game.
This pattern is useful even for things that are not really dependency injection. Suppose you work in finance, you might design some complicated logic for pricing an asset (a derivative say), which is all well and good and you can do without any stinking monads. But then, you modify your program to deal with multiple currencies. You need to be able to convert between currencies on the fly. Your first attempt is to define a top level function
type CurrencyDict = Map CurrencyName Dollars
currencyDict :: CurrencyDict
to get spot prices. You can then call this dictionary in your code....but wait! That won't work! The currency dictionary is immutable and so has to be the same not only for the life of your program, but from the time it gets compiled! So what do you do? Well, one option would be to use the Reader monad:
computePrice :: Reader CurrencyDict Dollars
computePrice
= do currencyDict <- ask
--insert computation here
Perhaps the most classic use-case is in implementing interpreters. But, before we look at that, we need to introduce another function
local :: (env -> env) -> Reader env a -> Reader env a
Okay, so Haskell and other functional languages are based on the lambda calculus. Lambda calculus has a syntax that looks like
data Term = Apply Term Term | Lambda String Term | Var Term deriving (Show)
and we want to write an evaluator for this language. To do so, we will need to keep track of an environment, which is a list of bindings associated with terms (actually it will be closures because we want to do static scoping).
newtype Env = Env ([(String, Closure)])
type Closure = (Term, Env)
When we are done, we should get out a value (or an error):
data Value = Lam String Closure | Failure String
So, let's write the interpreter:
interp' :: Term -> Reader Env Value
--when we have a lambda term, we can just return it
interp' (Lambda nv t)
= do env <- ask
return $ Lam nv (t, env)
--when we run into a value, we look it up in the environment
interp' (Var v)
= do (Env env) <- ask
case lookup (show v) env of
-- if it is not in the environment we have a problem
Nothing -> return . Failure $ "unbound variable: " ++ (show v)
-- if it is in the environment, then we should interpret it
Just (term, env) -> local (const env) $ interp' term
--the complicated case is an application
interp' (Apply t1 t2)
= do v1 <- interp' t1
case v1 of
Failure s -> return (Failure s)
Lam nv clos -> local (\(Env ls) -> Env ((nv, clos) : ls)) $ interp' t2
--I guess not that complicated!
Finally, we can use it by passing a trivial environment:
interp :: Term -> Value
interp term = runReader (interp' term) (Env [])
And that is it. A fully functional interpreter for the lambda calculus.
The other way to think about this is to ask: How is it implemented? The answer is that the reader monad is actually one of the simplest and most elegant of all monads.
newtype Reader env a = Reader {runReader :: env -> a}
Reader is just a fancy name for functions! We have already defined runReader so what about the other parts of the API? Well, every Monad is also a Functor:
instance Functor (Reader env) where
fmap f (Reader g) = Reader $ f . g
Now, to get a monad:
instance Monad (Reader env) where
return x = Reader (\_ -> x)
(Reader f) >>= g = Reader $ \x -> runReader (g (f x)) x
which is not so scary. ask is really simple:
ask = Reader $ \x -> x
while local isn't so bad:
local f (Reader g) = Reader $ \x -> runReader g (f x)
Okay, so the reader monad is just a function. Why have Reader at all? Good question. Actually, you don't need it!
instance Functor ((->) env) where
fmap = (.)
instance Monad ((->) env) where
return = const
f >>= g = \x -> g (f x) x
These are even simpler. What's more, ask is just id and local is just function composition with the order of the functions switched!
I remember being puzzled as you were, until I discovered on my own that variants of the Reader monad are everywhere. How did I discover it? Because I kept writing code that turned out to be small variations on it.
For example, at one point I was writing some code to deal with historical values; values that change over time. A very simple model of this is functions from points of time to the value at that point in time:
import Control.Applicative
-- | A History with timeline type t and value type a.
newtype History t a = History { observe :: t -> a }
instance Functor (History t) where
-- Apply a function to the contents of a historical value
fmap f hist = History (f . observe hist)
instance Applicative (History t) where
-- A "pure" History is one that has the same value at all points in time
pure = History . const
-- This applies a function that changes over time to a value that also
-- changes, by observing both at the same point in time.
ff <*> fx = History $ \t -> (observe ff t) (observe fx t)
instance Monad (History t) where
return = pure
ma >>= f = History $ \t -> observe (f (observe ma t)) t
The Applicative instance means that if you have employees :: History Day [Person] and customers :: History Day [Person] you can do this:
-- | For any given day, the list of employees followed by the customers
employeesAndCustomers :: History Day [Person]
employeesAndCustomers = (++) <$> employees <*> customers
I.e., Functor and Applicative allow us to adapt regular, non-historical functions to work with histories.
The monad instance is most intuitively understood by considering the function (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c. A function of type a -> History t b is a function that maps an a to a history of b values; for example, you could have getSupervisor :: Person -> History Day Supervisor, and getVP :: Supervisor -> History Day VP. So the Monad instance for History is about composing functions like these; for example, getSupervisor >=> getVP :: Person -> History Day VP is the function that gets, for any Person, the history of VPs that they've had.
Well, this History monad is actually exactly the same as Reader. History t a is really the same as Reader t a (which is the same as t -> a).
Another example: I've been prototyping OLAP designs in Haskell recently. One idea here is that of a "hypercube," which is a mapping from intersections of a set of dimensions to values. Here we go again:
newtype Hypercube intersection value = Hypercube { get :: intersection -> value }
One common of operation on hypercubes is to apply a multi-place scalar functions to corresponding points of a hypercube. This we can get by defining an Applicative instance for Hypercube:
instance Functor (Hypercube intersection) where
fmap f cube = Hypercube (f . get cube)
instance Applicative (Hypercube intersection) where
-- A "pure" Hypercube is one that has the same value at all intersections
pure = Hypercube . const
-- Apply each function in the #ff# hypercube to its corresponding point
-- in #fx#.
ff <*> fx = Hypercube $ \x -> (get ff x) (get fx x)
I just copypasted the History code above and changed names. As you can tell, Hypercube is also just Reader.
It goes on and on. For example, language interpreters also boil down to Reader, when you apply this model:
Expression = a Reader
Free variables = uses of ask
Evaluation environment = Reader execution environment.
Binding constructs = local
A good analogy is that a Reader r a represents an a with "holes" in it, that prevent you from knowing which a we're talking about. You can only get an actual a once you supply a an r to fill in the holes. There are tons of things like that. In the examples above, a "history" is a value that can't be computed until you specify a time, a hypercube is a value that can't be computed until you specify an intersection, and a language expression is a value that can't be computed until you supply the values of the variables. It also gives you an intuition on why Reader r a is the same as r -> a, because such a function is also intuitively an a missing an r.
So the Functor, Applicative and Monad instances of Reader are a very useful generalization for cases where you are modeling anything of the sort "an a that's missing an r," and allow you to treat these "incomplete" objects as if they were complete.
Yet another way of saying the same thing: a Reader r a is something that consumes r and produces a, and the Functor, Applicative and Monad instances are basic patterns for working with Readers. Functor = make a Reader that modifies the output of another Reader; Applicative = connect two Readers to the same input and combine their outputs; Monad = inspect the result of a Reader and use it to construct another Reader. The local and withReader functions = make a Reader that modifies the input to another Reader.
In Java or C++ you may access any variable from anywhere without any problem. Problems appears when your code becomes multi-threaded.
In Haskell you have only two ways to pass the value from one function to another:
You pass the value through one of input parameters of the callable function. Drawbacks are: 1) you can't pass ALL the variables in that way - list of input parameters just blow your mind. 2) in sequence of function calls: fn1 -> fn2 -> fn3, function fn2 may not need parameter which you pass from fn1 to fn3.
You pass the value in scope of some monad. Drawback is: you have to get firm understanding what Monad conception is. Passing the values around is just one of great deal of applications where you may use the Monads. Actually Monad conception is incredible powerful. Don't be upset, if you didn't get insight at once. Just keep trying, and read different tutorials. The knowledge you'll get will pay off.
The Reader monad just pass the data you want to share between functions. Functions may read that data, but can't change it. That's all that do the Reader monad. Well, almost all. There are also number of functions like local, but for the first time you can stick with asks only.

Resources