3
votes

I am currently working on the computation expression series of the fabulous fsharpforfunandprofit website and I have a question regarding the lesson 4 "wrapped type" of the computation series. I have tried reading a bit further , but there's an important notion I am not grasping.

Actually, I do understand the definition of bind :

member Bind : M<'T> * ('T -> M<'U>) -> M<'U>

but one thing I do not understand at this point is the magic when using it in a computation expression with let!:

For instance in :

let product'' = 
    dbresult {
        let! custId = getCustomerId "Alice"
        let! orderId = getLastOrderForCustomer "" // error!
        let! productId = getLastProductForOrder orderId 
        printfn "Product is %s" productId
        return productId
        }
printfn "%A" product''

getCustomerId "Alice" give me back M<'T> , but custId is already the unwrapped 'T and I cannot see anywhere how does this magic trick work...

Is it part of the code hidden in the let! instruction within Fsharp core assemblies? Could someone explain to me how the let! get the T' out of its wrapper?

Thanks for your explanations

1
this is exactly what Bind is used for (unwrapping): you can think of let! custId = ... as just Bind (getCustomerId "Alice") (fun custId -> ... the next line(s)) so each let! will get you another level of Bind - Random Dev
let! itself is syntactic-sugar that desugars to bind - you can find some details in this post from Don - Random Dev
OK got it! Thx a lot... - Arthis
want me to wrap it into an answer? ... one moment - Random Dev

1 Answers

6
votes

this:

let product'' = 
    dbresult {
        let! custId = getCustomerId "Alice"
        let! orderId = getLastOrderForCustomer "" // error!
        let! productId = getLastProductForOrder orderId 
        printfn "Product is %s" productId
        return productId
        }

will desugar to something like (naming the monad type as just DB<'t>):

let product'' = 
   DB.Delay(fun () ->
       DB.Bind(getCustomerId "Alice",(fun custId ->
          DB.Bind(getLastOrderForCustomer "",(fun orderId ->
             DB.Bind(getLastProductForOrder orderId, (fun productId ->
                printfn "Product is %s" productId
                DB.Return productId)))))))

so basically you get a Bind level for each let! (you can usually ignore the Delay)

As you can see the computational expression syntax is much nicer than the nested Binds - most languages which supports monadic expressions of some sort have similar syntactic sugar - even C# (from ... in ... select aka LINQ)