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
r
is a normal thing to do, and if so, in what circumstances this might be useful - or if the type variable
r
has to be read as for allr
and restricting the type ofr
will lead to all sorts of trouble.
Can someone shed some light on this?
Technically, I think it's fine. Specializing
Cont r a
toNum r => Cont r a
doesn't seem fundamentally more problematic than specializingReader r a
toNum 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 beid
or functions that don't terminate, as you have noted.A version of your
c1
usingmapCont
might 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.