Fsharpplus: F#+ generic computation expressions and Nested Monads

Created on 25 Aug 2020  路  10Comments  路  Source: fsprojects/FSharpPlus

Hello!!
First of All Thanks for this Beautiful library.
I m new to FP so forgive me if i'm not writing idiomatic code.
I m trying to wrap my head around F#+ generic computation expressions.

this is my understanding:
U don't need to create computation expressions for each monad, u can use generic computation expressions for all cases.

from Option to Async , but what about Async> ?
does F#+ generic computation expressions will work with nested monads ?

this is sample code I'm trying to work with but I'm getting following error

his expression was expected to have type
'Result'
but here has type
'string'

 let p():Async<Result<string, string>> = async { return Error ""}

    let m():Async<Result<string, string>> = monad{
        let! (e:string) = p() //This expression was expected to have type 'Result<string,string>'     but here has type 'string' 
        return e
    }
question

Most helpful comment

@aammfe see if you find this video useful:

F# Online - Josef Star媒chfojt暖 - FSharpPlus - Advanced FP concepts in F#
https://www.youtube.com/watch?v=pxJCHJgG8ws

All 10 comments

I'm unsure what you are getting at, but perhaps the examples from abstraction-monad can be helpful?

What you seem to be looking for might be:

```f#
let p():Async> = async { return Error ""}

let m():ResultT<Async<Result<string, string>>> = monad {
    let! (e:string) = p() |> lift
    return e
}

`` ? And then you'd useResultT.run`

does monad {} also work as asyncResult computation expression ?

I'm trying to get value from Async<Result> in computation expression.
let! (e:string) = p() but getting error This expression was expected to have type 'Result' but here has type 'string' seems like moand{} is just working as async not asyncResult computation expression.

so does generic computation expression work as asyncResult computation expression ?

let p():Async<Result<string, string>> = async { return Error ""}

    let m():Async<Result<string, string>> = monad{
        let! (e:string) = p() //This expression was expected to have type 'Result<string,string>'     but here has type 'string' 
        return e
    }

and please check m return type is not ResultT<Async<Result<string, string>>> but Async<Result<string, string>>

See my answers below:

U don't need to create computation expressions for each monad, u can use generic computation expressions for all cases.

Yes, normally with F#+ you don't need to create your monadic CEs by hand, unless there's something that requires some kind of customization.

from Option to Async , but what about Async does F#+ generic computation expressions will work with nested monads ?

Not directly. Monads are not out-of-the-box composables, you need to create a version of each monad that admits composition (except for the last monad in the composition).

That's what Monad Transformers do. In this library you'll find some of them ready to use, for you case, in order to compose Async with Result you will have to use the ResultT monad transformer, as @wallymathieu already suggested.

seems like moand{} is just working as async not asyncResult computation expression.

Yes, of course. The library (and then the compiler) can't guess your intention, if you want to derive a CE for a composed monad you have to tell him somehow.

So, here are two interesting conclusions.

  • When we compose abstractions we need to tell that we are composing them.
    Why? Because the bind operation is generic. If we create non generic operations this won't be necessary. I mean, we can have Option.bind, AsyncOption.bind and so on. But if we use a generic bind we need to use the type system to "tell" treat this as a composition. That's why we use wrapper types.

For instance when composing applicatives we wrap them with Comp.

Now with computation expressions we could define a monad2 generic CE that will expect 2 monads instead of one, but then we run into the next problem

  • Monads are not composable

Functors and applicatives are but monads are not, and sometimes there is more that a possible composition.

For those 2 reasons we have to use monad transformers, which at the same type are wrapper types, so they express the composition intention into the type system but also they come with a specific bind implementation which is specific to the composition.

If we don't use monad transformers we would need to add specific combinations, like asyncResult or RWS which is a Reader, Writer, State. This is something we can consider adding to this library as genericity alone is not the goal.

I hope to have clarified this question, which is a frequent one we developers pose. Otherwise please let me know.

Thanx for ur detailed explanation.

So functors and applicators can be combined but monad can be combined bcs of bind function that's why we have introduced monad transformers.
in my case ResultT is MT.

I realised that I need to learn more.
I have almost read fsharpforfunandprofit.com and his domain Modeling Made Functional book.
since I'm beginner to FP can u please suggest what should I learn ?

My suggestion is that if you're a beginner in FP, stay away for the moment from Monad Transformers.

FSharp for fun and profit is a good resource to learn how to use these advanced concepts but it would be nice to see how their examples get simplified by using this library. I would like to add some code snippets to their site.

Regarding monad transformers there is some basic docs here, as pointed by @wallymathieu but if you want to dig more you'll have to look at Haskell docs.

In anycase, you can always ask a specific question in our Slack or Gitter, also here, but the chats are better to get help on question like "what am I missing here / how can I achieve this?".

@aammfe see if you find this video useful:

F# Online - Josef Star媒chfojt暖 - FSharpPlus - Advanced FP concepts in F#
https://www.youtube.com/watch?v=pxJCHJgG8ws

Thanx for ur good advice

to look at Haskell docs.

so all routes to learn FP goes through haskell ?

@aammfe see if you find this video useful:

F# Online - Josef Star媒chfojt暖 - FSharpPlus - Advanced FP concepts in F#
https://www.youtube.com/watch?v=pxJCHJgG8ws

actually this video introduced me to F#+ in first place.

so all routes to learn FP goes through haskell ?

The abstractions have been implemented first in Haskell (as far as I know). There is a lot of fertile ground when it comes to functional programming and lisps. Prolog and logic programming should also be an interesting route.

Mark Seemann has an article series about these concepts as well

Mark Seemann has an article series about these concepts as well

Thanx man Mark seemann is a good teacher.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cmeeren picture cmeeren  路  9Comments

TOTBWF picture TOTBWF  路  7Comments

cmeeren picture cmeeren  路  7Comments

cannorin picture cannorin  路  3Comments

pangwa picture pangwa  路  11Comments