Can someone please help me understand this map definition in Professor Wadler's original paper Monads for Functional Programming (Haskell).
map :: (a → b) →(M a →M b)
map f m =m >= λa.unit(f a)
I understand why it is declared as a morphism from f::a -> b to g::Ma -> Mb. Why is it confusingly defined as seemingly taking 2 args f and m. m is a computation ( function with side effects) that I assume can be defined as data or type.
A definition of the form
foo x y z = bar
is equivalent to all of the following ones
foo x y = \z -> bar
foo x = \y z -> bar
foo = \x y z -> bar
Hence, the posted code could also be written as
map :: (a → b) → (M a → M b)
map f = \m -> m >= \a -> unit (f a)
-- which is parsed as
-- map f = \m -> (m >= (\a -> (unit (f a))))
The above indeed emphasizes that map maps functions to functions, and is arguably clearer. However, it is a bit more verbose, so it is common in Haskell to move the arguments to the left side of = as much as possible.
The second argument is the first argument for the returned function:
map : (a -> b) -> m a -> m b
map = \(f : a -> b) -> \(x : m a) ->
x >>= (\a -> return (f a))
Related
I am an intermediate schemer, but only a haskell beginner. Here is my problem:
Suppose you have an associative binary operation, says (>>=). Is there a polyvariadic function p such that p (>>=) h g f e = h >>= g >>= f >>= e?
I am asking this question because this question says it is possible if the binary operation takes inputs of the same type. I wonder if this can be generalized.
EDIT-1: I try to modify the code in http://okmij.org/ftp/Haskell/vararg-fn.lhs (the section of Variable number of variably typed arguments) with little progress.
EDIT-2: Simplify the code a bit.
{-# LANGUAGE FunctionalDependencies, FlexibleInstances #-}
module Main where
class Lfold f a b | b -> a where
lfold :: (a -> (f a) -> (f a)) -> (f a) -> a -> b
instance Lfold f a (f a) where
lfold op rid x = op x rid
instance Lfold f a b => Lfold f a (a -> b) where
lfold op rid x y = lfold op (op x rid) y
test :: [String]
test = lfold (:) [] "a" "b" "c"
main :: IO ()
main = putStrLn $ show test
Yes, you can create such a function. It is very ugly however, and you will need to explicitly type every argument you are going to pass to make the compiler find the correct instance.
Starting from the polyvariadic function template you linked, I arrived at
{-# LANGUAGE FlexibleInstances, InstanceSigs, MultiParamTypeClasses #-}
class ImplicitChain m a r where
p :: m a -> r
instance Monad m => ImplicitChain m a (m a) where
p :: m a -> m a
p x = x
instance (Monad m, ImplicitChain m b r) => ImplicitChain m a ((a -> m b) -> r) where
p :: m a -> (a -> m b) -> r
p x f = p (x >>= f)
h :: Int -> [Int]
h = replicate 2
g :: Int -> [Int]
g = (:[])
f :: Int -> [Int]
f = flip enumFromTo 2
test :: [Int]
test = p [1::Int] h g f
But you were asking whether we can do more generic, so that the binary operation is an argument as well. Yes:
{-# LANGUAGE FlexibleInstances, InstanceSigs, MultiParamTypeClasses, UndecidableInstances #-}
class ImplicitVariadic a b r where
p :: (a -> b -> a) -> r
instance ImplicitVariadic a b (a -> a) where
p :: (a -> b -> a) -> a -> a
p _ x = x
instance (ImplicitVariadic a b (a -> r)) => ImplicitVariadic a b (a -> b -> r) where
p :: (a -> b -> a) -> a -> b -> r
p f x y = p f (f x y)
You can't (at least, not easily), because you need to know how many arguments you are getting ahead of time. Because all functions in Haskell are automatically curried, every function takes exactly one argument and returns one value. Even a simple binary operator takes one argument (the first operand) and returns a function that takes one argument (the second operand) and returns a result. That is,
a + b == (+) a b
== ((+) a) b
There is no way for your imaginary function p to know from its first argument how many other arguments are going to be given. That is, what should the type of p be?
p :: (a -> a -> a) -> a -- zero arguments?
p :: (a -> a -> a) -> a -> a -- one argument?
p :: (a -> a -> a) -> a -> a -> a -- two arguments?
p :: (a -> a -> a) -> a -> a -> a -> a -- three arguments?
Instead, the best you can do is use a fold, which takes an operation and a list of operands.
foldr (+) 0 [h, g, f, e] == h + g + f + e + 0 -- explicit first argument of 0
foldr1 (+) [h, g, f, e] == h + g + f + e -- assumes a list of at least one value
To see what I mean by "not easily", look at the implementation of printf in the Text.Printf module. Even that is not a good example, because the first argument carries information (the number of placeholders in the format string) that a binary operation alone does not.
Can someone help me with writing my own version of a (.) function in Haskell?
From this post Haskell write your version of a ($) function I know how to determine a type of this function, but I still have the problem with its body.
I also do not know why ghci refuses to use the name (..).
($$$) :: (b -> c) -> (a -> b) -> a -> c
($$$) f (g x) = ((f g) $) x
infixr 9 $$$
Another idea of mine was for instance this one:
($$$) :: (b -> c) -> (a -> b) -> a -> c
($$$) f (g x) = map (f) (g x)
infixr 9 $$$
The error message says that "Parse error in pattern: g".
From the signature:
($$$) :: (b -> c) -> (a -> b) -> a -> c
your function needs 3 arguments. So I would start:
($$$) f g x = ...
| | \
| \ a
| \
| a->b
b->c
Update
This attempt at defining ($$$) does not work:
($$$) (f g) x = ...
It says that ($$$) takes two arguments. The way I've started to define ($$$) says that the function takes three arguments.
Are you coming from Lisp? You still seem to assume lists everywhere...
As I already said in the other thread, lists have nothing to do with this task, so neither of (:), foldr or map can possibly be useful here.
More to the point, the occurence of (g x) in the left-hand side of the definition doesn't make sense. (This is not a list, but apparently you think it should be a kind of “argument list”).
As a matter of fact, you could define ($$$) in un-curried form this way:
($$$) :: (b->c) -> (a->b, a) -> c
($$$) f (g, x) = ...
...which is exactly the same thing as the more elegant
f $$$ (g, x) = ...
In this case, you have an argument tuple (g, x), which is more or less equivalent to a Lisp list.
In Haskell, we like to write functions curried though. The signature
($$$) :: (b -> c) -> (a -> b) -> a -> c
is in fact parsed as
($$$) :: (b -> c) -> ( (a -> b) -> (a -> c) )
Hence the way to define such a function is, at the most fundamental level
($$$) = \f -> (\g -> (\x -> ... ))
Which can be written short as
($$$) f g x = ...
or
(f $$$ g) x = ...
In the actual definition part, you should similarly get the grasp of how things are actually parsed. As you have by now figured out, the composition operator can be defined as
($$$) f g x = f(g(x))
In fact, only the outer parentheses are necessary here: the preferred form is
($$$) f g x = f (g x)
or indeed
($$$) f g x = f $ g x
If something like g x or (f g) appears on its own in an expression, it always means that the left function is applied to the right argument. For f g this doesn't make sense, because though f is a function it can not take another function as its argument, only the result of such a function. Well, to get such a result you need to apply g to an argument!
Many thanks for your patience. I used brackets where I should not have. I still have problems with looking at everything (including mathematical operators) as a function. Now the idea of (.) is clear.
($$$) :: (b -> c) -> (a -> b) -> a -> c
($$$) f g x = f(g x)
infixr 9 $$$
I've been using the Free datatype in Control.Monad.Free from the free package. Now I'm trying to convert it to use F in Control.Monad.Free.Church but can't figure out how to map the functions.
For example, a simple pattern matching function using Free would look like this -
-- Pattern match Free
matchFree
:: (a -> r)
-> (f (Free f a) -> r)
-> Free f a
-> r
matchFree kp _ (Pure a) = kp a
matchFree _ kf (Free f) = kf f
I can easily convert it to a function that uses F by converting to/from Free -
-- Pattern match F (using toF and fromF)
matchF
:: Functor f
=> (a -> r)
-> (f (F f a) -> r)
-> F f a
-> r
matchF kp kf = matchF' . fromF
where
matchF' (Pure a) = kp a
matchF' (Free f) = kf (fmap toF f)
However I can't figure out how to get it done without using toF and fromF -
-- Pattern match F (without using toF)???
-- Doesn't compile
matchF
:: Functor f
=> (a -> r)
-> (f (F f a) -> r)
-> F f a
-> r
matchF kp kf f = f kp kf
There must be a general pattern I am missing. Can you help me figure it out?
You asked for the "general pattern you are missing". Let me give my own attempt at explaining it, though Petr Pudlák's answer is also pretty good. As user3237465 says, there are two encodings that we can use, Church and Scott, and you're using Scott rather than Church. So here's the general review.
How encodings work
By continuation passing, we can describe any value of type x by some unique function of type
data Identity x = Id { runId :: x }
{- ~ - equivalent to - ~ -}
newtype IdentityFn x = IdFn { runIdFn :: forall z. (x -> z) -> z }
The "forall" here is very important, it says that this type leaves z as an unspecified parameter. The bijection is that Id . ($ id) . runIdFn goes from IdentityFn to Identity while IdFn . flip ($) . runId goes the other way. The equivalence comes because there is essentially nothing one can do with the type forall z. z, no manipulations are sufficiently universal. We can equivalently state that newtype UnitFn = UnitFn { runUnitFn :: forall z. z -> z } has only one element, namely UnitFn id, which means that it corresponds to the unit type data Unit = Unit in a similar way.
Now the currying observation that (x, y) -> z is isomorphic to x -> y -> z is the tip of a continuation-passing iceberg which allows us to represent data structures in terms of pure functions, with no data structures, because clearly the type Identity (x, y) is equivalent therefore to forall z. (x -> y -> z) -> z. So "gluing" together two items is the same as creating a value of this type, which just uses pure functions as "glue".
To see this equivalence, we have to just handle two other properties.
The first is sum-type constructors, in the form of Either x y -> z. See, Either x y -> z is isomorphic to
newtype EitherFn x y = EitherFn { runEitherFn :: forall z. (x -> z) -> (y -> z) -> z }
from which we get the basic idea of the pattern:
Take a fresh type variable z that does not appear in the body of the expression.
For each constructor of the data type, create a function-type which takes all of its type-arguments as parameters, and returns a z. Call these "handlers" corresponding to the constructors. So the handler for (x, y) is (x, y) -> z which we curry to x -> y -> z, and the handlers for Left x | Right y are x -> z and y -> z. If there are no parameters, you can just take a value z as your function rather than the more cumbersome () -> z.
Take all of those handlers as parameters to an expression forall z. Handler1 -> Handler2 -> ... -> HandlerN -> z.
One half of the isomorphism is basically just to hand the constructors in as the desired handlers; the other pattern-matches on the constructors and applies the correponding handlers.
Subtle missing things
Again, it's fun to apply these rules to various things; for example as I noted above, if you apply this to data Unit = Unit you find that any unit type is the identity function forall z. z -> z, and if you apply this to data Bool = False | True you find the logic functions forall z. z -> z -> z where false = const while true = const id. But if you do play with it you will notice that something's missing still. Hint: if we look at
data List x = Nil | Cons x (List x)
we see that the pattern should look like:
data ListFn x = ListFn { runListFn :: forall z. z -> (x -> ??? -> z) -> z }
for some ???. The above rules don't pin down what goes there.
There are two good options: either we use the power of the newtype to its fullest to put ListFn x there (the "Scott" encoding), or we can preemptively reduce it with the functions we've been given, in which case it becomes a z using the functions that we already have (the "Church" encoding). Now since the recursion is already being performed for us up-front, the Church encoding is only perfectly equivalent for finite data structures; the Scott encoding can handle infinite lists and such. It can also be hard to understand how to encode mutual recursion in the Church form whereas the Scott form is usually a little more straightforward.
Anyway, the Church encoding is a little harder to think about, but a little more magical because we get to approach it with wishful thinking: "assume that this z is already whatever you're trying to accomplish with tail list, then combine it with head list in the appropriate way." And this wishful thinking is precisely why people have trouble understanding foldr, as the one side of this bijection is precisely the foldr of the list.
There are some other problems like "what if, like Int or Integer, the number of constructors is big or infinite?". The answer to this particular question is to use the functions
data IntFn = IntFn { runIntFn :: forall z. (z -> z) -> z -> z }
What is this, you ask? Well, a smart person (Church) has worked out that this is a way to represent integers as the repetition of composition:
zero f x = x
one f x = f x
two f x = f (f x)
{- ~ - increment an `n` to `n + 1` - ~ -}
succ n f = f . n f
Actually on this account m . n is the product of the two. But I mention this because it is not too hard to insert a () and flip arguments around to find that this is actually forall z. z -> (() -> z -> z) -> z which is the list type [()], with values given by length and addition given by ++ and multiplication given by >>.
For greater efficiency, you might Church-encode data PosNeg x = Neg x | Zero | Pos x and use the Church encoding (keeping it finite!) of [Bool] to form the Church encoding of PosNeg [Bool] where each [Bool] implicitly ends with an unstated True at its most-significant bit at the end, so that [Bool] represents the numbers from +1 to infinity.
An extended example: BinLeaf / BL
One more nontrivial example, we might think about the binary tree which stores all of its information in leaves, but also contains annotations on the internal nodes: data BinLeaf a x = Leaf x | Bin a (BinLeaf a x) (BinLeaf a x). Following the recipe for Church encoding we do:
newtype BL a x = BL { runBL :: forall z. (x -> z) -> (a -> z -> z -> z) -> z}
Now instead of Bin "Hello" (Leaf 3) (Bin "What's up?" (Leaf 4) (Leaf 5) we construct instances in lowercase:
BL $ \leaf bin -> bin "Hello" (leaf 3) (bin "What's up?" (leaf 4) (leaf 5)
The isomorphism is thus very easy one way: binleafFromBL f = runBL f Leaf Bin. The other side has a case dispatch, but is not too bad.
What about recursive algorithms on the recursive data? This is where it gets magical: foldr and runBL of Church encoding have both run whatever our functions were on the subtrees before we get to the trees themselves. Suppose for example that we want to emulate this function:
sumAnnotate :: (Num n) => BinLeaf a n -> BinLeaf (n, a) n
sumAnnotate (Leaf n) = Leaf n
sumAnnotate (Bin a x y) = Bin (getn x' + getn y', a) x' y'
where x' = sumAnnotate x
y' = sumAnnotate y
getn (Leaf n) = n
getn (Bin (n, _) _ _) = n
What do we have to do?
-- pseudo-constructors for BL a x.
makeLeaf :: x -> BL a x
makeLeaf x = BL $ \leaf _ -> leaf x
makeBin :: a -> BL a x -> BL a x -> BL a x
makeBin a l r = BL $ \leaf bin -> bin a (runBL l leaf bin) (runBL r leaf bin)
-- actual function
sumAnnotate' :: (Num n) => BL a n -> BL n n
sumAnnotate' f = runBL f makeLeaf (\a x y -> makeBin (getn x + getn y, a) x y) where
getn t = runBL t id (\n _ _ -> n)
We pass in a function \a x y -> ... :: (Num n) => a -> BL (n, a) n -> BL (n, a) n -> BL (n, a) n. Notice that the two "arguments" are of the same type as the "output" here. With Church encoding, we have to program as if we've already succeeded -- a discipline called "wishful thinking".
The Church encoding for the Free monad
The Free monad has normal form
data Free f x = Pure x | Roll f (Free f x)
and our Church encoding procedure says that this becomes:
newtype Fr f x = Fr {runFr :: forall z. (x -> z) -> (f z -> z) -> z}
Your function
matchFree p _ (Pure x) = p x
matchFree _ f (Free x) = f x
becomes simply
matchFree' p f fr = runFr fr p f
Let me describe the difference for a simpler scenario - lists. Let's focus on how one can consume lists:
By a catamorphism, which essentially means that we can express it using
foldr :: (a -> r -> r) -> r -> [a] -> r
As we can see, the folding functions never get hold of the list tail, only its processed value.
By pattern matching we can do somewhat more, in particular we can construct a generalized fold of type
foldrGen :: (a -> [a] -> r) -> r -> [a] -> r
It's easy to see that one can express foldr using foldrGen. However, as foldrGen isn't recursive, this expression involves recursion.
To generalize both concepts, we can introduce
foldrPara :: (a -> ([a], r) -> r) -> r -> [a] -> r
which gives the consuming function even more power: Both the reduced value of the tail, as well as the tail itself. Clearly this is more generic than both previous ones. This corresponds to a paramorphism which “eats its argument and keeps it too”.
But it's also possible to do it the other way round. Even though paramorphisms are more general, they can be expressed using catamorphisms (at some overhead cost) by re-creating the original structure on the way:
foldrPara :: (a -> ([a], r) -> r) -> r -> [a] -> r
foldrPara f z = snd . foldr f' ([], z)
where
f' x t#(xs, r) = (x : xs, f x t)
Now Church-encoded data structures encode the catamorphism pattern, for lists it's everything that can be constructed using foldr:
newtype List a = L (forall r . r -> (a -> r -> r) -> r)
nil :: List a
nil = L $ \n _ -> n
cons :: a -> List a -> List a
cons x (L xs) = L $ \n c -> c x (xs n c)
fromL :: List a -> [a]
fromL (L f) = f [] (:)
toL :: [a] -> List a
toL xs = L (\n c -> foldr c n xs)
In order to see the sub-lists, we have take the same approach: re-create them on the way:
foldrParaL :: (a -> (List a, r) -> r) -> r -> List a -> r
foldrParaL f z (L l) = snd $ l (nil, z) f'
where
f' x t#(xs, r) = (x `cons` xs, f x t)
This applies generally to Church-encoded data structures, like to the encoded free monad. They express catamorphisms, that is folding without seeing the parts of the structure, only with the recursive results. To get hold of sub-structures during the process, we need to recreate them on the way.
Your
matchF
:: Functor f
=> (a -> r)
-> (f (F f a) -> r)
-> F f a
-> r
looks like the Scott-encoded Free monad. The Church-encoded version is just
matchF
:: Functor f
=> (a -> r)
-> (f r -> r)
-> F f a
-> r
matchF kp kf f = runF f kp kf
Here are Church- and Scott-encoded lists for comparison:
newtype Church a = Church { runChurch :: forall r. (a -> r -> r) -> r -> r }
newtype Scott a = Scott { runScott :: forall r. (a -> Scott a -> r) -> r -> r }
It's a bit of a nasty one. This problem is a more general version of a puzzle everyone struggles with the first time they're exposed to it: defining the predecessor of a natural number encoded as a Church numeral (think: Nat ~ Free Id ()).
I've split my module into a lot of intermediate definitions to highlight the solution's structure. I've also uploaded a self-contained gist for ease of use.
I start with nothing exciting: redefining F given that I don't have this package installed at the moment.
{-# LANGUAGE Rank2Types #-}
module MatchFree where
newtype F f a = F { runF :: forall r. (a -> r) -> (f r -> r) -> r }
Now, even before considering pattern-matching, we can start by defining the counterpart of the usual datatype's constructors:
pureF :: a -> F f a
pureF a = F $ const . ($ a)
freeF :: Functor f => f (F f a) -> F f a
freeF f = F $ \ pr fr -> fr $ fmap (\ inner -> runF inner pr fr) f
Next, I'm introducing two types: Open and Close. Close is simply the F type but Open corresponds to having observed the content of an element of F f a: it's Either a pure a or an f (F f a).
type Open f a = Either a (f (F f a))
type Close f a = F f a
As hinted by my hand-wavy description, these two types are actually equivalent and we can indeed write functions converting back and forth between them:
close :: Functor f => Open f a -> Close f a
close = either pureF freeF
open :: Functor f => Close f a -> Open f a
open f = runF f Left (Right . fmap close)
Now, we can come back to your problem and the course of action should be pretty clear: open the F f a and then apply either kp or kf depending on what we got. And it indeed works:
matchF
:: Functor f
=> (a -> r)
-> (f (F f a) -> r)
-> F f a
-> r
matchF kp kf = either kp kf . open
Coming back to the original comment about natural numbers: predecessor implemented using Church numeral is linear in the size of the natural number when we could reasonably expect a simple case analysis to be constant time. Well, just like for natural numbers, this case analysis is pretty expensive because, as show by the use of runF in the definition of open, the whole structure is traversed.
I was reviewing some code and came across the following gem, which I'd wager is a copy-paste of pointfree output:
(I thought the following would more appropriate than the usual foo/bar for this particular question :P)
import Control.Monad (liftM2)
data Battleship = Battleship { x :: Int
, y :: Int
} deriving Show
placeBattleship :: Int -> Int -> Battleship
placeBattleship x' y' = Battleship { x = x', y = y' }
coordinates :: Battleship -> (Int, Int)
coordinates = liftM2 (,) x y
Would someone be kind enough to explain the steps needed to simplify from: (i) coordinates b = (x b, y b) to: (ii) coordinates = liftM2 (,) x y? In particular, I'm a bit confused as to the use of liftM2 as I wasn't even aware that a monad was lurking in the background.
I know that (i) can also be represented as: coordinates s = (,) (x s) (y s) but I'm not sure where/how to proceed.
P.S. The following is why I suspect it's from pointfree (output is from GHCI and :pl is aliased to pointfree):
λ: :pl coordinates s = (x s, y s)
coordinates = liftM2 (,) x y
This takes advantage of the Monad instance for (->) r, also called the "reader monad". This is the monad of functions from a specific type to a. (Take a look here for motivation on why it exists in the first place.)
To see how it works for various functions, replace m with (r -> in m a. For example, if we just do liftM, we get:
liftM :: (a -> b) -> (m a -> m b)
liftM :: (a -> b) -> ((r -> a) -> (r -> b))
:: (a -> b) -> (r -> a) -> (r -> b) -- simplify parentheses
...which is just function composition. Neat.
We can do the same thing for liftM2:
liftM2 :: (a -> b -> c) -> m a -> m b -> m c
liftM2 :: (a -> b -> c) -> (r -> a) -> (r -> b) -> (r -> c)
So what we see is a way to compose two one-argument functions with a two-argument function. It's a way of generalizing normal function composition to more than one argument. The idea is that we create a function that takes a single r by passing that through both of the one-argument functions, getting two arguments to pass into the two-argument function. So if we have f :: (r -> a), g :: (r -> b) and h :: (a -> b -> c), we produce:
\ r -> h (f r) (h r)
Now, how does this apply to your code? (,) is the two-argument function, and x and y are one-argument functions of the type Battleship -> Int (because that's how field accessors work). With this in mind:
liftM2 (,) x y = \ r -> (,) (x r) (y r)
= \ r -> (x r, y r)
Once you've internalized the idea of multiple function composition like this, point-free code like this becomes quite a bit more readable—no need to use the pointfree tool! In this case, I think the non-pointfree version is still better, but the pointfree one isn't terrible itself.
The monad liftM2 is working over here is the function monad (->) a. This is equivalent to the Reader monad, as you may have seen before.
Recall the definition of liftM2:
liftM2 :: Monad m => (a -> b -> r) -> m a -> m b -> m r
liftM2 f ma mb = do
a <- ma
b <- mb
return $ f a b
So now if we substitute in (,) for f, x for ma, and y for mb, we get
liftM2 (,) x y = do
a <- x
b <- y
return $ (,) a b
Since x, y :: Battleship -> Int which is equivalent to ((->) Battleship) Int, then m ~ (->) Battleship. The function monad is defined as
instance Monad ((->) a) where
return x = const x
m >>= f = \a -> f (m a) a
Essentially what the function monad does is allow you to extract the output from several functions provided they all have the same input. A more clear example might be something like
test = do
a <- (^2)
b <- (^3)
c <- (^4)
d <- show
return (a, b, c, d)
> test 2
(4, 8, 16, "2")
You could easily rewrite
data Battleship = Battleship { x :: Int
, y :: Int
} deriving Show
placeBattleship :: Int -> Int -> Battleship
placeBattleship x y = Battleship x y
coordinates :: Battleship -> (Int, Int)
coordinates (Battleship x y) = (x, y)
It isn't point-free style, but quite simple
Suppose we have a monad, defined by return, (>>=) and the set of laws. There is a data type
newtype C m a = C { unC ∷ forall r. (a → m r) → m r }
also known as Codensity. C m a ≅ m a given that m is a Monad, i.e. we can write two functions to ∷ Monad m ⇒ m a → C m a and from ∷ Monad m ⇒ C m a → m a
to ∷ Monad m ⇒ m a → C m a
to t = C $ \f → t >>= f
from ∷ Monad m ⇒ C m a → m a
from = ($ return) . unC
and show that to ∘ from ≡ id and from ∘ to ≡ id by equational reasoning, for example:
from . to = -- by definition of `(.)'
\x → from (to x) = -- by definition of `to'
\x → from (C $ \f → x >>= f) = -- by definition of `from'
\x → ($ return) (unC (C $ \f → x >>= f)) = -- unC . C ≡ id
\x → ($ return) (\f → x >>= f) = -- β-reduce
\x → x >>= return = -- right identity law
\x → x = -- by definition of `id'
id
So far so good. My questions are
Given a type and a bunch of laws, how do we construct corresponding isomorphic CPS representation?
Is this representation unique (I'd guess no)?
If it's not unique, is there always the most "simple" (in the number of →s for example :) ) one?
No such encoding is Unique
As you posed the question the answer is obviously "no"
a = forall r. (a -> r) -> r
a = forall s. ((forall r. (a -> r) -> r) -> s) -> s
as to which encoding is the smallest...well the underlying type is almost certainly!
Codensity might not be what you want
whats more, although Codensity is fascinating, I don't believe it is isomorphic to the underlying type. from . to = id is the easy direction.
to . from
= \x -> to (from x)
= \x -> C $ \f -> (from x) >>= f
= \x -> C $ \f -> (unC x return) >>= f
= \(C g) -> C $ \f -> (g return) >>= f
but then you get kinda stuck. The same thing happens when you try to prove a = forall r. (a -> r) -> r but you get saved by the "theorem for free" (there might be a way to do it without this, but the free theorem makes it easy). I know of no corresponding argument for Codensity, and what most papers I have read prove is rather that it preserves >>= and return, that is, if you only construct your C m a using the monadic operations and what you call to then the call to to . from is identity.
If we try hard enough we can even come up with a counter example to the isomorphism
evil :: C Maybe Int
evil = C $ \h -> case h 1 of
Nothing -> h 2
Just x -> Nothing
to . from $ evil
= (\(C g) -> C $ \f -> (g return) >>= f) evil
= C $ \f -> ((\h -> case h 1 of
Nothing -> h 2
Just x -> Nothing) return) >>= f
= C $ \f -> Nothing >>= f
so are these the same?
test 1 = Nothing
test n = Just n
unC evil test
= Just 2
unC (C $ \f -> Nothing >>= f) test
= Nothing >>= test
= Nothing
I might have made a mistake in that derivation. I have not really checked it, but suffice it to say that right now I don't think C m a = m a
Another Kind of CPS
All data can be encoded as untyped lambda functions, a property discovered by Church about 70 years ago. Often we talk about "Church encoding" data structures, although Oleg has suggested that instead, at least in a typed setting, we should talk about "Boehm-Beraducci" encodings. Regardless of what you call it, the idea is
(a,b) = forall r. (a -> b -> r) -> r
Either a b = forall r. (a -> r) -> (b -> r) -> r
at least up to fast and loose reasoning. It should be obvious that this encoding provides a way to encode any ADT as a System F type. This also reveals a way of implementing functional languages: treat everything as closures under the hood, and have pattern matching just be implemented as function application.
Actually, System F even has a way of encoding existential types as universal types
exists a. f a = forall r. (forall a'. f a' -> r) -> r
which turns out to be a very important identity. Among other things, this helps us to think about the relation between type inference for higher rank types and for existential types. Since type inference is decidable up to rank 2 types, type inference is also decidable in a system with rank 1 universals and existentials. Since existential quantification is the basis for modules, this is important stuff.