Related
Using the following catamorphism for natural numbers I can implement various arithmetic algorithms whithout having to deal with recursion:
cataNat :: b -> (b -> b) -> Natural -> b
cataNat zero succ = go
where
go n = if (n <= 0) then zero else succ (go (n - 1))
fib :: Natural -> Natural
fib = fst . cataNat (0, 1) (\(a, b) -> (b, a + b))
cataNat looks similar to primitive recursion to me. At least each application of it seems garuanteed to terminate, no matter which combination of zero and succ is provided. With each iteration the overall problem is decomposed by the smallest/simplest problem instance. So even if it is technically not primitive recursion it seems to be equally expressive. If this is true it would mean that a catamorphism is not enough to express general recursion. We would probably need a hylomorphism for that. Is my reasoning correct, that is, does the equivalence hold for any type of catamorphism, not just for natural numbers?
Primitive recursion corresponds directly to a paramorphism.
You're correct that a catamorphism has equivalent theoretical power to a paramorphism, but they can be different in important ways in operational terms. For an example, let's go to lists instead of Nats.
cata :: b -> (a -> b -> b) -> [a] -> b
cata = flip foldr -- I'm lazy, but this argument order makes a bit more sense for this example
para :: b -> (a -> [a] -> b -> b) -> [a] -> b
para z _ [] = z
para z f (x:xs) = f x xs (para z f xs)
-- Removes the first element from the list which is equal to the other argument
delete1 :: Eq a => a -> [a] -> [a]
delete1 x xs = cata (const []) (\el k found -> if not found && el == x then k True else el : k found) xs False
-- Removes the first element from the list which is equal to the other argument
delete2 :: Eq a => a -> [a] -> [a]
delete2 x xs = para [] (\el raw processed -> if el == x then raw else el : processed) xs
Look at how awkward delete1 is, compared to delete2. Not only do you have to contort your logic by making the result of cata a function, but there's a very real operational cost, too. You have to traverse everything in the list after finding a matching element, and re-create all the (:) constructors. That can have a noticeable cost in efficiency. In comparison, delete2, when it finds the target element, can just use the existing tail of the list for the remainder, without even looking at it. Of course, most uses of foldr (real world, not this example) don't produce a function and don't want access to the unprocessed tail of the list. For them, the catamorphism is going to be slightly more efficient simply because of passing around less data.
So in terms of theoretical power, they're equivalent. In operational terms, each has a use, though catamorphisms are a lot more common.
For some expansion of the idea in more general terms, see the recursion-schemes library. It uses a rather different-looking formulation of the idea so that it can abstract over data types with different shapes, instead of needing a different type for cata/para for each data type they can be applied to. But it really is just an alternate way of packing up the same ideas, and other kinds of morphisms are covered as well, including much more niche (or even possibly useless) ones.
I have troubles understanding the implementation of the foldl function using foldr. I have read this question (Writing foldl using foldr) and I still have some things I don't understand in the following example:
fun :: [Int] -> [Int]
fun l = foldr g (const []) l 1
where g x f lst = if gcd x lst == 1 then x : f x else f lst
The function takes a list as parameter and return another list where gcd(l[i], l[i + 1] = 1.
My questions are the following:
1. Who are x, f and lst
2. What is const[] and why I can't use the id function?
foldr is one of those weird tools like bicycles that are really easy to use once you get the hang of them but hard to learn from the start. After several years of experience, I've gotten really good at spotting problems I can solve with foldr, and solving them with it immediately and correctly, but it could easily take me a while to figure out what just what I've done in enough detail to explain!
From a practical standpoint, I usually think of foldr in vaguely continuation-passing language. Ignoring the "simple" case where foldr is only applied to three arguments, an application of foldr looks like this:
foldr go finish xs acc1 acc2 ... where
finish acc1 acc2 ... = ?
go x cont acc1 acc2 ... = ?
acc1, etc., are accumulators passed "from left to right". The result consists, conceptually, of a single value passed "from right to left".
finish gets the final values of the accumulators and produces something of the result type. It's usually the easiest part to write because
foldr go finish [] acc1 acc2 ...
=
finish acc1 acc2 ...
So once you figure out just what you want your fold to produce, writing finish is fairly mechanical.
go gets a single container element, a "continuation", and the accumulators. It passes modified values if those accumulators "forward" to the continuation to get a result, and uses that result to construct its own.
foldl is a particularly simple case because its go function just returns the result it gets from folding the rest of the container with a new accumulator argument. I think it's a bit more instructive to look at an example that does a bit more. Here's one that takes a container of numbers and produces a list of pairs representing a running sum and a running product.
sumsProducts :: (Num n, Foldable f) => f n -> [(n, n)]
sumsProducts xs = foldr go finish xs 0 1
where
finish total prod = [(total, prod)]
go x cont total prod =
(total, prod) : cont (x + total) (x * prod)
foldr's type signature is this
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
This means your foldr applied to its 3 arguments must return a function that takes the 1 as an argument.
So you can specialise your foldr to this
foldr :: (Int -> (Int -> [Int]) -> (Int -> [Int]))
-> (Int -> [Int])
-> [Int]
-> (Int -> [Int])
This means your g function must have the following type
g :: Int -> (Int -> [Int]) -> Int -> [Int]
So your parameters have the type
x :: Int
f :: Int -> [Int]
lst :: Int
And foldr in its 2nd argument requires a Int -> [Int] instead of just an Int, so you can't pass it the value [].
Fortunately const returns a function that ignores its argument and just always return a constant expression
const [] :: a -> [b]
In your case f is indeed some kind of accumulator. But instead of reducing e.g. a list of values to some number, you are chaining functions here. By passing 1 to this function chain in the end, it gets evaluated and is then building the actual list you return in fun.
So I have a list of a functions of two arguments of the type [a -> a -> a]
I want to write a function which will take the list and compose them into a chain of functions which takes length+1 arguments composed on the left. For example if I have [f,g,h] all of types [a -> a -> a] I need to write a function which gives:
chain [f,g,h] = \a b c d -> f ( g ( h a b ) c ) d
Also if it helps, the functions are commutative in their arguments ( i.e. f x y = f y x for all x y ).
I can do this inside of a list comprehension given that I know the the number of functions in question, it would be almost exactly like the definition. It's the stretch from a fixed number of functions to a dynamic number that has me stumped.
This is what I have so far:
f xs = f' xs
where
f' [] = id
f' (x:xs) = \z -> x (f' xs) z
I think the logic is along the right path, it just doesn't type-check.
Thanks in advance!
The comment from n.m. is correct--this can't be done in any conventional way, because the result's type depends on the length of the input list. You need a much fancier type system to make that work. You could compromise in Haskell by using a list that encodes its length in the type, but that's painful and awkward.
Instead, since your arguments are all of the same type, you'd be much better served by creating a function that takes a list of values instead of multiple arguments. So the type you want is something like this: chain :: [a -> a -> a] -> [a] -> a
There are several ways to write such a function. Conceptually you want to start from the front of the argument list and the end of the function list, then apply the first function to the first argument to get something of type a -> a. From there, apply that function to the next argument, then apply the next function to the result, removing one element from each list and giving you a new function of type a -> a.
You'll need to handle the case where the list lengths don't match up correctly, as well. There's no way around that, other than the aforementioned type-encoded-lengths and the hassle associate with such.
I wonder, whether your "have a list of a functions" requirement is a real requirement or a workaround? I was faced with the same problem, but in my case set of functions was small and known at compile time. To be more precise, my task was to zip 4 lists with xor. And all I wanted is a compact notation to compose 3 binary functions. What I used is a small helper:
-- Binary Function Chain
bfc :: (c -> d) -> (a -> b -> c) -> a -> b -> d
bfc f g = \a b -> f (g a b)
For example:
ghci> ((+) `bfc` (*)) 5 3 2 -- (5 * 3) + 2
17
ghci> ((+) `bfc` (*) `bfc` (-)) 5 3 2 1 -- ((5 - 3) * 2) + 1
5
ghci> zipWith3 ((+) `bfc` (+)) [1,2] [3,4] [5,6]
[9,12]
ghci> getZipList $ (xor `bfc` xor `bfc` xor) <$> ZipList [1,2] <*> ZipList [3,4] <*> ZipList [5,6] <*> ZipList [7,8]
[0,8]
That doesn't answers the original question as it is, but hope still can be helpful since it covers pretty much what question subject line is about.
I'm very new to Haskell and FP in general. I've read many of the writings that describe what currying is, but I haven't found an explanation to how it actually works.
Here is a function: (+) :: a -> (a -> a)
If I do (+) 4 7, the function takes 4 and returns a function that takes 7 and returns 11. But what happens to 4 ? What does that first function do with 4? What does (a -> a) do with 7?
Things get more confusing when I think about a more complicated function:
max' :: Int -> (Int -> Int)
max' m n | m > n = m
| otherwise = n
what does (Int -> Int) compare its parameter to? It only takes one parameter, but it needs two to do m > n.
Understanding higher-order functions
Haskell, as a functional language, supports higher-order functions (HOFs). In mathematics HOFs are called functionals, but you don't need any mathematics to understand them. In usual imperative programming, like in Java, functions can accept values, like integers and strings, do something with them, and return back a value of some other type.
But what if functions themselves were no different from values, and you could accept a function as an argument or return it from another function? f a b c = a + b - c is a boring function, it sums a and b and then substracts c. But the function could be more interesting, if we could generalize it, what if we'd want sometimes to sum a and b, but sometimes multiply? Or divide by c instead of subtracting?
Remember, (+) is just a function of 2 numbers that returns a number, there's nothing special about it, so any function of 2 numbers that returns a number could be in place of it. Writing g a b c = a * b - c, h a b c = a + b / c and so on just doesn't cut it for us, we need a general solution, we are programmers after all! Here how it is done in Haskell:
let f g h a b c = a `g` b `h` c in f (*) (/) 2 3 4 -- returns 1.5
And you can return functions too. Below we create a function that accepts a function and an argument and returns another function, which accepts a parameter and returns a result.
let g f n = (\m -> m `f` n); f = g (+) 2 in f 10 -- returns 12
A (\m -> m `f` n) construct is an anonymous function of 1 argument m that applies f to that m and n. Basically, when we call g (+) 2 we create a function of one argument, that just adds 2 to whatever it receives. So let f = g (+) 2 in f 10 equals 12 and let f = g (*) 5 in f 5 equals 25.
(See also my explanation of HOFs using Scheme as an example.)
Understanding currying
Currying is a technique that transforms a function of several arguments to a function of 1 argument that returns a function of 1 argument that returns a function of 1 argument... until it returns a value. It's easier than it sounds, for example we have a function of 2 arguments, like (+).
Now imagine that you could give only 1 argument to it, and it would return a function? You could use this function later to add this 1st argument, now encased in this new function, to something else. E.g.:
f n = (\m -> n - m)
g = f 10
g 8 -- would return 2
g 4 -- would return 6
Guess what, Haskell curries all functions by default. Technically speaking, there are no functions of multiple arguments in Haskell, only functions of one argument, some of which may return new functions of one argument.
It's evident from the types. Write :t (++) in interpreter, where (++) is a function that concatenates 2 strings together, it will return (++) :: [a] -> [a] -> [a]. The type is not [a],[a] -> [a], but [a] -> [a] -> [a], meaning that (++) accepts one list and returns a function of type [a] -> [a]. This new function can accept yet another list, and it will finally return a new list of type [a].
That's why function application syntax in Haskell has no parentheses and commas, compare Haskell's f a b c with Python's or Java's f(a, b, c). It's not some weird aesthetic decision, in Haskell function application goes from left to right, so f a b c is actually (((f a) b) c), which makes complete sense, once you know that f is curried by default.
In types, however, the association is from right to left, so [a] -> [a] -> [a] is equivalent to [a] -> ([a] -> [a]). They are the same thing in Haskell, Haskell treats them exactly the same. Which makes sense, because when you apply only one argument, you get back a function of type [a] -> [a].
On the other hand, check the type of map: (a -> b) -> [a] -> [b], it receives a function as its first argument, and that's why it has parentheses.
To really hammer down the concept of currying, try to find the types of the following expressions in the interpreter:
(+)
(+) 2
(+) 2 3
map
map (\x -> head x)
map (\x -> head x) ["conscience", "do", "cost"]
map head
map head ["conscience", "do", "cost"]
Partial application and sections
Now that you understand HOFs and currying, Haskell gives you some syntax to make code shorter. When you call a function with 1 or multiple arguments to get back a function that still accepts arguments, it's called partial application.
You understand already that instead of creating anonymous functions you can just partially apply a function, so instead of writing (\x -> replicate 3 x) you can just write (replicate 3). But what if you want to have a divide (/) operator instead of replicate? For infix functions Haskell allows you to partially apply it using either of arguments.
This is called sections: (2/) is equivalent to (\x -> 2 / x) and (/2) is equivalent to (\x -> x / 2). With backticks you can take a section of any binary function: (2`elem`) is equivalent to (\xs -> 2 `elem` xs).
But remember, any function is curried by default in Haskell and therefore always accepts one argument, so sections can be actually used with any function: let (+^) be some weird function that sums 4 arguments, then let (+^) a b c d = a + b + c in (2+^) 3 4 5 returns 14.
Compositions
Other handy tools to write concise and flexible code are composition and application operator. Composition operator (.) chains functions together. Application operator ($) just applies function on the left side to the argument on the right side, so f $ x is equivalent to f x. However ($) has the lowest precedence of all operators, so we can use it to get rid of parentheses: f (g x y) is equivalent to f $ g x y.
It is also helpful when we need to apply multiple functions to the same argument: map ($2) [(2+), (10-), (20/)] would yield [4,8,10]. (f . g . h) (x + y + z), f (g (h (x + y + z))), f $ g $ h $ x + y + z and f . g . h $ x + y + z are equivalent, but (.) and ($) are different things, so read Haskell: difference between . (dot) and $ (dollar sign) and parts from Learn You a Haskell to understand the difference.
You can think of it like that the function stores the argument and returns a new function that just demands the other argument(s). The new function already knows the first argument, as it is stored together with the function. This is handled internally by the compiler. If you want to know how this works exactly, you may be interested in this page although it may be a bit complicated if you are new to Haskell.
If a function call is fully saturated (so all arguments are passed at the same time), most compilers use an ordinary calling scheme, like in C.
Does this help?
max' = \m -> \n -> if (m > n)
then m
else n
Written as lambdas. max' is a value of a lambda that itself returns a lambda given some m, which returns the value.
Hence max' 4 is
max' 4 = \n -> if (4 > n)
then 4
else n
Something that may help is to think about how you could implement curry as a higher order function if Haskell didn't have built in support for it. Here is a Haskell implementation that works for a function on two arguments.
curry :: (a -> b -> c) -> a -> (b -> c)
curry f a = \b -> f a b
Now you can pass curry a function on two arguments and the first argument and it will return a function on one argument (this is an example of a closure.)
In ghci:
Prelude> let curry f a = \b -> f a b
Prelude> let g = curry (+) 5
Prelude> g 10
15
Prelude> g 15
20
Prelude>
Fortunately we don't have to do this in Haskell (you do in Lisp if you want currying) because support is built into the language.
If you come from C-like languages, their syntax might help you to understand it. For example in PHP the add function could be implemented as such:
function add($a) {
return function($b) use($a) {
return $a + $b;
};
}
Haskell is based on Lambda calculus. Internally what happens is that everything gets converted into a function. So your compiler evaluates (+) as follows
(+) :: Num a => a -> a -> a
(+) x y = \x -> (\y -> x + y)
That is, (+) :: a -> a -> a is essentially the same as (+) :: a -> (a -> a). Hope this helps.
This is how the Cont monad is defined:
newtype Cont r a = Cont { runCont :: (a -> r) -> r }
instance Monad (Cont r) where
return a = Cont ($ a)
m >>= k = Cont $ \c -> runCont m $ \a -> runCont (k a) c
Could you explain how and why this works? What is it doing?
The first thing to realize about the continuation monad is that, fundamentally, it's not really doing anything at all. It's true!
The basic idea of a continuation in general is that it represents the rest of a computation. Say we have an expression like this: foo (bar x y) z. Now, extract just the parenthesized portion, bar x y--this is part of the total expression, but it's not just a function we can apply. Instead, it's something we need to apply a function to. So, we can talk about the "rest of the computation" in this case as being \a -> foo a z, which we can apply to bar x y to reconstruct the complete form.
Now, it happens that this concept of "the rest of the computation" is useful, but it's awkward to work with, since it's something outside of the subexpression we're considering. To make things work better, we can turn things inside-out: extract the subexpression we're interested in, then wrap it in a function that takes an argument representing the rest of the computation: \k -> k (bar x y).
This modified version gives us a lot of flexibility--not only does it extract a subexpression from its context, but it lets us manipulate that outer context within the subexpression itself. We can think of it as a sort of suspended computation, giving us explicit control over what happens next. Now, how could we generalize this? Well, the subexpression is pretty much unchanged, so let's just replace it with a parameter to the inside-out function, giving us \x k -> k x--in other words, nothing more than function application, reversed. We could just as easily write flip ($), or add a bit of an exotic foreign language flavor and define it as an operator |>.
Now, it would be simple, albeit tedious and horribly obfuscating, to translate every piece of an expression to this form. Fortunately, there's a better way. As Haskell programmers, when we think building a computation within a background context the next thing we think is say, is this a monad? And in this case the answer is yes, yes it is.
To turn this into a monad, we start with two basic building blocks:
For a monad m, a value of type m a represents having access to a value of type a within the context of the monad.
The core of our "suspended computations" is flipped function application.
What does it mean to have access to something of type a within this context? It just means that, for some value x :: a, we've applied flip ($) to x, giving us a function that takes a function which takes an argument of type a, and applies that function to x. Let's say we have a suspended computation holding a value of type Bool. What type does this give us?
> :t flip ($) True
flip ($) True :: (Bool -> b) -> b
So for suspended computations, the type m a works out to (a -> b) -> b... which is perhaps an anticlimax, since we already knew the signature for Cont, but humor me for now.
An interesting thing to note here is that a sort of "reversal" also applies to the monad's type: Cont b a represents a function that takes a function a -> b and evaluates to b. As a continuation represents "the future" of a computation, so the type a in the signature represents in some sense "the past".
So, replacing (a -> b) -> b with Cont b a, what's the monadic type for our basic building block of reverse function application? a -> (a -> b) -> b translates to a -> Cont b a... the same type signature as return and, in fact, that's exactly what it is.
From here on out, everything pretty much falls directly out from the types: There's essentially no sensible way to implement >>= besides the actual implementation. But what is it actually doing?
At this point we come back to what I said initially: the continuation monad isn't really doing much of anything. Something of type Cont r a is trivially equivalent to something of just type a, simply by supplying id as the argument to the suspended computation. This might lead one to ask whether, if Cont r a is a monad but the conversion is so trivial, shouldn't a alone also be a monad? Of course that doesn't work as is, since there's no type constructor to define as a Monad instance, but say we add a trivial wrapper, like data Id a = Id a. This is indeed a monad, namely the identity monad.
What does >>= do for the identity monad? The type signature is Id a -> (a -> Id b) -> Id b, which is equivalent to a -> (a -> b) -> b, which is just simple function application again. Having established that Cont r a is trivially equivalent to Id a, we can deduce that in this case as well, (>>=) is just function application.
Of course, Cont r a is a crazy inverted world where everyone has goatees, so what actually happens involves shuffling things around in confusing ways in order to chain two suspended computations together into a new suspended computation, but in essence, there isn't actually anything unusual going on! Applying functions to arguments, ho hum, another day in the life of a functional programmer.
Here's fibonacci:
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
Imagine you have a machine without a call stack - it only allows tail recursion. How to execute fib on that machine? You could easily rewrite the function to work in linear, instead of exponential time, but that requires tiny bit of insight and is not mechanical.
The obstacle to making it tail recursive is the third line, where there are two recursive calls. We can only make a single call, which also has to give the result. Here's where continuations enter.
We'll make fib (n-1) take additional parameter, which will be a function specifying what should be done after computing its result, call it x. It will be adding fib (n-2) to it, of course. So: to compute fib n you compute fib (n-1) after that, if you call the result x, you compute fib (n-2), after that, if you call the result y, you return x+y.
In other words you have to tell:
How to do the following computation: "fib' n c = compute fib n and apply c to the result"?
The answer is that you do the following: "compute fib (n-1) and apply d to the result", where d x means "compute fib (n-2) and apply e to the result", where e y means c (x+y). In code:
fib' 0 c = c 0
fib' 1 c = c 1
fib' n c = fib' (n-1) d
where d x = fib' (n-2) e
where e y = c (x+y)
Equivalently, we can use lambdas:
fib' 0 = \c -> c 0
fib' 1 = \c -> c 1
fib' n = \c -> fib' (n-1) $ \x ->
fib' (n-2) $ \y ->
c (x+y)
To get actual Fibonacci use identity: fib' n id. You can think that the line fib (n-1) $ ... passes its result x to the next one.
The last three lines smell like a do block, and in fact
fib' 0 = return 0
fib' 1 = return 1
fib' n = do x <- fib' (n-1)
y <- fib' (n-2)
return (x+y)
is the same, up to newtypes, by definition of monad Cont. Note differences. There's \c -> at the beginning, instead of x <- ... there's ... $ \x -> and c instead of return.
Try writing factorial n = n * factorial (n-1) in a tail recursive style using CPS.
How does >>= work? m >>= k is equivalent to
do a <- m
t <- k a
return t
Making the translation back, in the same style as in fib', you get
\c -> m $ \a ->
k a $ \t ->
c t
simplifying \t -> c t to c
m >>= k = \c -> m $ \a -> k a c
Adding newtypes you get
m >>= k = Cont $ \c -> runCont m $ \a -> runCont (k a) c
which is on top of this page. It's complex, but if you know how to translate between do notation and direct use, you don't need to know exact definition of >>=! Continuation monad is much clearer if you look at do-blocks.
Monads and continuations
If you look at this usage of list monad...
do x <- [10, 20]
y <- [3,5]
return (x+y)
[10,20] >>= \x ->
[3,5] >>= \y ->
return (x+y)
([10,20] >>=) $ \x ->
([3,5] >>=) $ \y ->
return (x+y)
that looks like continuation! In fact, (>>=) when you apply one argument has type (a -> m b) -> m b which is Cont (m b) a. See sigfpe's Mother of all monads for explanation. I'd regard that as a good continuation monad tutorial, though it wasn't probably meant as it.
Since continuations and monads are so strongly related in both directions, I think what applies to monads applies to continuations: only hard work will teach you them, and not reading some burrito metaphor or analogy.
EDIT: Article migrated to the link below.
I've written up a tutorial directly addressing this topic that I hope you will find useful. (It certainly helped cement my understanding!) It's a bit too long to fit comfortably in a Stack Overflow topic, so I've migrated it to the Haskell Wiki.
Please see: MonadCont under the hood
I think the easiest way to get a grip on the Cont monad is to understand how to use its constructor. I'm going to assume the following definition for now, although the realities of the transformers package are slightly different:
newtype Cont r a = Cont { runCont :: (a -> r) -> r }
This gives:
Cont :: ((a -> r) -> r) -> Cont r a
so to build a value of type Cont r a, we need to give a function to Cont:
value = Cont $ \k -> ...
Now, k itself has type a -> r, and the body of the lambda needs to have type r. An obvious thing to do would be to apply k to a value of type a, and get a value of type r. We can do that, yes, but that's really only one of many things we can do. Remember that value need not be polymorphic in r, it might be of type Cont String Integer or something else concrete. So:
We could apply k to several values of type a, and combine the results somehow.
We could apply k to a value of type a, observe the result, and then decide to apply k to something else based on that.
We could ignore k altogether and just produce a value of type r ourselves.
But what does all this mean? What does k end up being? Well, in a do-block, we might have something looking like this:
flip runCont id $ do
v <- thing1
thing2 v
x <- Cont $ \k -> ...
thing3 x
thing4
Here's the fun part: we can, in our minds and somewhat informally, split the do-block in two at the occurrence of the Cont constructor, and think of the rest of the entire computation after it as a value in itself. But hold on, what it is depends on what x is, so it's really a function from a value x of type a to some result value:
restOfTheComputation x = do
thing3 x
thing4
In fact, this restOfTheComputation is roughly speaking what k ends up being. In other words, you call k with a value which becomes the result x of your Cont computation, the rest of the computation runs, and then the r produced winds its way back into your lambda as the result of the call to k. So:
if you called k multiple times, the rest of the computation will get run multiple times, and the results may be combined however you wish.
if you didn't call k at all, the rest of the entire computation will be skipped, and the enclosing runCont call will just give you back whatever value of type r you managed to synthesise. That is, unless some other part of the computation is calling you from their k, and messing about with the result...
If you're still with me at this point it should be easy to see this could be quite powerful. To make the point a little, let's implement some standard type classes.
instance Functor (Cont r) where
fmap f (Cont c) = Cont $ \k -> ...
We're given a Cont value with bind result x of type a, and a function f :: a -> b, and we want to make a Cont value with bind result f x of type b. Well, to set the bind result, just call k...
fmap f (Cont c) = Cont $ \k -> k (f ...
Wait, where do we get x from? Well, it's going to involve c, which we haven't used yet. Remember how c works: it gets given a function, and then calls that function with its bind result. We want to call our function with f applied to that bind result. So:
fmap f (Cont c) = Cont $ \k -> c (\x -> k (f x))
Tada! Next up, Applicative:
instance Applicative (Cont r) where
pure x = Cont $ \k -> ...
This one's simple. We want the bind result to be the x we get.
pure x = Cont $ \k -> k x
Now, <*>:
Cont cf <*> Cont cx = Cont $ \k -> ...
This a little trickier, but uses essentially the same ideas as in fmap: first get the function from the first Cont, by making a lambda for it to call:
Cont cf <*> Cont cx = Cont $ \k -> cf (\fn -> ...
Then get the value x from the second, and make fn x the bind result:
Cont cf <*> Cont cx = Cont $ \k -> cf (\fn -> cx (\x -> k (fn x)))
Monad is much the same, although requires runCont or a case or let to unpack the newtype.
This answer is already quite long, so I won't go into ContT (in short: it is exactly the same as Cont! The only difference is in the kind of the type constructor, the implementations of everything are identical) or callCC (a useful combinator that provides a convenient way to ignore k, implementing early exit from a sub-block).
For a simple and plausible application, try Edward Z. Yang's blog post implementing labelled break and continue in for loops.
Trying to complement the other answers:
Nested lambdas are horrible for readability. This is exactly the reason why let... in... and ... where ... exist, to get rid of nested lambdas by using intermediate variables. Using those, the bind implementation can be refactored into:
newtype Cont r a = Cont { runCont :: (a -> r) -> r }
instance Monad (Cont r) where
return a = Cont ($ a)
m >>= k = k a
where a = runCont m id
Which hopefully makes what is happening clearer. The return implementation boxes value with a lazy apply. Using runCont id applies id to the boxed value, which returns the original value.
For any monad where any boxed value can simply be unboxed, there is generally a trivial implementation of bind, which is to simply unbox the value and apply a monadic function to it.
To get the obfuscated implementation in the original question, first replace k a with Cont $ runCont (k a) , which in turn can be replaced with Cont $ \c-> runCont (k a) c
Now, we can move the where into a subexpression, so that we are left with
Cont $ \c-> ( runCont (k a) c where a = runCont m id )
The expression within parentheses can be desugared into \a -> runCont (k a) c $ runCont m id.
To finish, we use the property of runCont, f ( runCont m g) = runCont m (f.g), and we are back to the original obfuscated expression.