Reputation: 57159
If the answer to this is "you are going about it all wrong", by all means let me know a more proper way. I had code which was structured like this:
type Res<'T> =
| E of (DC -> 'T)
| V of 'T
Now this type was primarily used directly, with a lot of inline code that did manual per-case binding, which is a lot of boilerplate I am trying to get rid of, so I figured I turn it into a computation expression. It wasn't too hard to get map, bind and apply right.
State was carried along through DC but this was cumbersome to get right, so I changed it to the following, which is a common signature I see in discussions with the state monad.
type Res<'T> =
| E of (DC -> DC * 'T)
| V of 'T
Then I decided to take it to the next level and introduce the Delayed monad (or Eventually or whatever its name is). So I changed my type as follows, making it recursive:
type Res<'T> =
| E of (DC -> DC * Res<'T>)
| V of 'T
I tried some variants of the following, but keep getting:
The resulting type would be infinite when unifying ''a' and 'Res<'a> -> Res<'b>'
(usually happens when I do not invoke the return, i.e. the Res.E <|
below)
Or I can't seem to get my types right, the following (understandably) throws a type error, but I'm having a blind spot fixing it:
This expression was expected to have type 'Res<'a>' but here has type 'DC * Res<'a>'.
let rec bind k res =
match res with
| Res.V v -> k v // not delayed, should it be?
| Res.E e ->
Res.E <| fun dc -> bind k (e dc) // here's my error, on 'e dc'
//(fun dc -> dc, bind f (e dc))
bind k res
I understand that the signature should be ('a -> Res<'b>) -> Res<'a> -> XRes<'b>
. The problem comes from getting the recursion right, at least in my head. I'm not even sure why I am feeding the result to Res.E
, as in my understanding, the k
continuation should already return this type anyway.
Also, I currently have the return as follows (following Steve Horsfield):
let result v = Res.V v
But also noticed in some posts (notably the hilarious Frankenfunctor) this:
let result v = Res.E <| fun dc -> dc, Res.V v
Maybe I am following a pattern I shouldn't have, but it seemed a good idea at the time, especially in trying to refactor zillions of boilerplate code and replace it with computation expressions, but perhaps I should stick to the direct evaluation, it fit in my head ;).
But I feel like I'm close... Any ideas?
Upvotes: 2
Views: 130
Reputation: 80744
I'm not quite sure what the meaning of this monad would be, but
Ok, after thinking about it a bit and reading the question's header :- ), I do understand what it means, and I can see how to build bind
for it.
Your mistake is that the function wrapped in Res.E
should return a tuple, but you're having it return just Res<'b>
by recursively calling bind
. And to mirror that, you're passing the result of e
to recursive bind
call in place of Res<'b>
parameter, while e
actually returns a tuple.
To do this correctly, you need to destructure the result of e
, then pass second part to recursive bind
call, and pair its result with the first part:
let rec bind (k: 'a -> 'b Res) (res: 'a Res) : 'b Res =
match res with
| V a -> k a
| E e ->
E <| fun dc ->
let dc', res' = e dc
dc', bind k res'
The terminal case, I don't believe should be also delayed. That is, I don't see a reason for it. As far as I understand, the interpretation of the V x
case should be "a computation that doesn't change state and returns x
", in which case making it delayed doesn't add anything but an extra lambda-expression.
Upvotes: 1