the Cont r a type stands for a function which takes a continuation a->r and produces a result of type r. So both the continuation and the entire Cont r a produce a result of the same type r.
My question is: are the two results necessarily the same value, or can a Cont r a post-process the result from the continuation and produce a different value, albeit of the same type r?
I tried using (+1) for post-processing (note the + 1 --<--):
c1 :: Int -> Cont r Int
c1 x = let y = 2*x
in cont $ \k -> (k y) + 1 --<--
Now that doesn't typecheck, because my post-processing function (+1) only accepts an argument whose type belongs to the Num typeclass. However, I pass the result of the continuation (k y) which is of some type r that is not guaranteed to belong to the Num typeclass.
Whatever I do to (k y), it must be a function of type r->r. The only function which can do this for all r is the id function and using id for post-processing is no post-processing at all.
However, the whole thing does typecheck if I restrict r to the Num typeclass or even to the concrete type Int. It then produces the expected result:
*Main> runCont (c1 1) id
3
I am quite unsure,
- if such post-processing and restricting the type of
ris a normal thing to do, and if so, in what circumstances this might be useful - or if the type variable
rhas to be read as for allrand restricting the type ofrwill lead to all sorts of trouble.
Can someone shed some light on this?
Technically, I think it's fine. Specializing
Cont r atoNum r => Cont r adoesn't seem fundamentally more problematic than specializingReader r atoNum r => Reader r a.An implication of doing so is that the resulting CPS computation can only be run against a (final) continuation that produces a number, but that's obvious -- if you have a computation that post-processes the continuation result as a number, it can only be used with continuations that produce numbers!
As additional evidence that this is sanctioned at least to some degree, note that there's a function:
If this function was to be used with no restriction on
r, the only valid values for its first argument would beidor functions that don't terminate, as you have noted.A version of your
c1usingmapContmight look like:and seems to work fine:
As for when this would be useful, I'm not sure. I can think of a few somewhat lame applications. You could define an computation that overrides the final result (provided no other kind of post-processing is used):
to be used like:
or a computation transformer that emulates a writer to add log functionality:
which you might use like this:
yielding:
These don't seem like very compelling applications, though.