I found Tomas's answer helpful, with a few important caveats:
Using the name Cont to represent a computation is IMHO confusing
because it suggests (incorrectly) that a Cont is a contination. I
propose to use the name Inc instead, because it represents an
"incomplete" computation. This computation contains a value (of type
'T) that it is ready to pass on to a continuation. Important point:
An Inc is not the same thing as a continuation.
I would also propose to define Inc without the pattern matching
overhead of a discriminated union. This simplifies the implementation
of Bind considerably.
I'm also not sure why we should assume that a continuation always
produces unit, but it certainly simplifies things considerably, so
I'll keep this assumption in what follows.
So, we can define a continuation as any function with a signature of 'T -> unit, and we can define Inc as:
type Inc<'T> =
('T -> unit) -> unit
In English, an Inc passes its wrapped 'T value to a given continuation function, resulting in unit.
Next, we need an explicit signature for the Bind function. Because it's a monad, we know it has to look like this:
let bind (inc : Inc<'T>) (wrap : 'T -> Inc<'U>) : Inc<'U> =
So bind takes an incomplete computation (of type 'T) and a function that can "wrap" a raw value in an incomplete computation (of type 'U), and returns a new incomplete computation of that type. Based on this signature, we know that bind has to return an Inc, and an Inc is a function that takes a continuation as input. So we know that implementation of bind has to start like this:
fun (cont : 'U -> unit) -> ...
Our job is to extract the 'T value that's wrapped inside the given Inc, and then re-wrap it using the given wrapper function. Key insight: The only way to obtain this value is to ask the Inc to pass it to us via a continuation that we'll now write! Inside this "artificial" continuation, we re-wrap the extracted value and give it back to the caller. The completed bind function therefore looks like this:
let bind (inc : Inc<'T>) (wrap : 'T -> Inc<'U>) : Inc<'U> =
fun (cont : 'U -> unit) -> // return an Inc, which is a function that takes a continuation as input
inc (fun t -> // force the incomplete computation to cough up its wrapped value
(wrap t) cont) // wrap the raw value so it can be sent to the given continuation