3

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

Arthis
  • 2,283
  • 21
  • 32
  • 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 May 19 '15 at 09:27
  • `let!` itself is *syntactic-sugar* that *desugars* to bind - you can find some details in this [post from Don](http://blogs.msdn.com/b/dsyme/archive/2007/09/22/some-details-on-f-computation-expressions-aka-monadic-or-workflow-syntax.aspx) – Random Dev May 19 '15 at 09:30
  • want me to wrap it into an answer? ... one moment – Random Dev May 19 '15 at 09:32

1 Answers1

6

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)

Random Dev
  • 51,810
  • 9
  • 92
  • 119
  • The thing I forgot was what the bind method bind(m,f) was actually doing. When matching m it applies f to a and thus unwrap the value for future use. Thanks you very much. – Arthis May 19 '15 at 09:43