Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slow compiler performance in Monad Transformers sample with Visual Studio, and can't give hints. #24

Open
OnurGumus opened this issue Dec 14, 2016 · 16 comments

Comments

@OnurGumus
Copy link

OnurGumus commented Dec 14, 2016

When I try Monad Tranformers sample with Visual Studio 2015 along with F# PowerTools, I noticed that the CPU peaks when IDE sits idle, probably due to type inference engine and syntax colorization is delayed for few seconds.

I am not sure if this issue is related to F# compiler , VisualFSharp or FSharpPlus. Can you suggest me any workarounds like putting a hint to make IDE happy for better performance ?
Also when I explicitly give type like below:

let getValidPassword : ErrorT<Async<_>> =
monad {
    let! s = liftAsync getLine
    if isValid s then return s
    else return! throw -1
}
</ catch /> (fun s -> throw ("The error was: " + decodeError s))

Notice Error<Async<_>>

`I get errors like below :

  • Could not resolve the ambiguity inherent in the use of the operator 'Bind' at or near this program point. Consider using type annotations to resolve the ambiguity.
  • Could not resolve the ambiguity inherent in the use of the operator 'LiftAsync' at or near this program point. Consider using type annotations to resolve the ambiguity.
  • Type constraint mismatch when applying the default type 'obj' for a type inference variable. No overloads match for method 'LiftAsync'. The available overloads are shown below (or in the Error List window). Consider adding further type constraints

Why is that so ?

@gusty gusty changed the title Slow performance in Monad Transformers sample with Visual Studio, and can't give hints. Slow compiler performance in Monad Transformers sample with Visual Studio, and can't give hints. Dec 14, 2016
@gusty
Copy link
Member

gusty commented Dec 14, 2016

There is a known issue in the F# compiler which makes overload resolution very slow in cases like this.

Fortunately this was solved in F# 4.1 so if you want to play with this at a decent compiler speed will need to wait for Visual Studio RC2 or download the compiler from github.

Regarding the type annotation yes, they are tricky. That's most likely something that can be improved in the F# compiler as well but the current status is that the less you tell the smarter they are, otherwise you have to go to full type annotations:

let getValidPassword : ErrorT<Async<Choice<string,string>>> =
    monad {
        let! s = lift getLine : ErrorT<Async<Choice<string,int>>>
        if isValid s then return s
        else return! (throw -1 : ErrorT<Async<Choice<string,int>>>) }
    </catch/>
        (fun s -> throw ("The error was: " + decodeError s))

let askPassword : ErrorT<Async<Choice<string,string>>> = monad {
    do! ( (lift <| putStrLn "Insert your new password:") : ErrorT<Async<Choice<unit,string>>>)
    let! value = getValidPassword : ErrorT<Async<Choice<string,string>>>
    do! lift <| putStrLn "Storing in database..." : ErrorT<Async<Choice<unit,string>>>
    return value}

But that's not a good workaround and it doesn't speed up a lot. The problem is the use of the generic code, in this sample the code is extremely generic, even more generic that the Haskell version since catch changes the type of the error.

A good workaround for this is not to use generic code, however in Monad Transformers it's difficult not to use generic code at all because you will need concrete functions for each combinations of monads.

Still it would be possible to create less generic code in idiomatic F# style. Something like ErrorT.catch instead of just catch indeed there are already many functions like ErrorT.map and so on but we need to add more.

When I got time I will add more less generic functions.

Finally note that Monad Transformers are really experimental though I started a project that makes heavy use of them and the compile time is not an issue when using F# 4.1.

Thanks for the report.

@gusty
Copy link
Member

gusty commented Dec 21, 2016

@OnurGumus Visual Studio RC2 is already released as an update for the original RC.
I tried it and the overall speed up is awesome.
If you have the chance to try it and report back the results would be much appreciated.
We'll need to state in the requirements for this project that F# 4.1 is highly recommended if you plan to use generic code.

@OnurGumus
Copy link
Author

@gmpl I have tried RC with update. I admit it is better but still intellisense get stuck a bit. CPU is not constantly high but when ever I press dot it peaks for a while. So I would say you should still consider some improvements in FSharp plus if you can.

@gusty
Copy link
Member

gusty commented Dec 22, 2016

@OnurGumus I also noticed that intellisense get stuck for one or two seconds from time to time in RC2, but not just intellisense, the whole GUI is blocked.
That's a VS bug that needs to be reported, because before it wasn't like that.
Before intellisense was way slower but didn't block the GUI.

Regarding what we can do on FSharpPlus side is to add less generic functions for each module.
I will be working on that soon.

I would be interested in knowing what is your use case for the monad transformers, it would be great if you could share that information and possibly would guide the way to improve them.

@OnurGumus
Copy link
Author

OnurGumus commented Dec 22, 2016

@gmpl Also giving all hints to the file as you described almost fixes the issue. Currently I don't have a particular use case, but previously I attempted to use a state monad. Of course then I needed async monad as well. Thus there happened a need to compromise these workflows.

I think monad transformers fills an important gap in F# compiler. Once you start these workflows, heavily it is inevitable that you will need these transformers. So I really applaud your effort, and hoping you will make this framework better.

By the way Actually the performance is not terrible even acceptable now

@gusty
Copy link
Member

gusty commented Dec 22, 2016

@OnurGumus Thank you very much for your feedback. I started this library for my personal use at the very beginning but soon I realized that it would very limited if it's not a community effort.
So, feel free to open more issues, participate in the discussions and eventually submit code and samples.

Right now there is a very important design decision coming which proposes to merge FsControl here.

Apart from that there are many improvements coming, specially now with the new F# 4.1, the goal is to finally reach a stable version in 2 or 3 months.

@OnurGumus
Copy link
Author

Neah, Why not go bolder instead? I'd say this stuff should be part of the compiler. Don Sym is very open minded. Why don't you discuss this? You can start by opening a suggestion. I would vote for it.

@gusty
Copy link
Member

gusty commented Dec 22, 2016

I'm 100% sure he will disagree. Historically he was always against using static constraints for stuff other than generic math code.

I see his point, he thinks that this should be done 'the right way' by implementing Type Classes at the language level and Higher Kinds at the CLR level.

But we are merely mortals and we don't have the power to change the language and even less the CLR, so that's why we have this project which let us play with the abstractions those nice features would bring, without having to wait for years until MS Gods finally implement them.

Not only that, the day those features are there, this library can be easily adapted to take advantage of such new constructs.

Anyway, the first step would be to have more projects adopting this library, in the sense that more projects integrate with it, more people collaborate and we can possibly move it to FsProjects, that way it's clear that it's a community effort and not just my personal project.

@OnurGumus
Copy link
Author

Not relevant to this issue but you might find this very interesting :
http://alxandr.me/2016/08/31/crazy-things-you-can-do-with-the-f-ternary-operator
See how he abused (?<-) to achieve ad hoc polymorphism. Might be handy for fsharpplus too

@gusty
Copy link
Member

gusty commented Dec 27, 2016

Actually that's how this projects started at the very beginning.

See my old blog which explore this technique.

Note that my post with the same examples about the ternary operator is 5 years old.
While writing about that technique I started a project which later was the inspiration for FsControl which now is part of FSharpPlus.

If you read the note on that old project, in the section UPDATE I explained there why I stopped using the ternary operator.

So, that was the very early beginning but since then this technique evolved a lot, some limitations in the F# compiler were fixed and this evolution lead to more features, like default methods, 'clean signatures' and more interestingly come up with an architecture which allows easy integration with other projects.

Now, from time to time I see blog posts like that one where people seems to 're-discover' this technique, but most of the time the readers comment that the same technique is used here and that post is not the exception if you read their comments.

Finally, if you want to see how to really abuse of the ternary operator, read my previous entries on my blog, specifically the post about functions for tuples abuses even more of this technique in order to create structures that even in Haskell are not possible to encode, unless you have dependent types.

@OnurGumus
Copy link
Author

Wow, interesting history.
By the way, Perhaps some more important is on the way, regarding the completion block
dotnet/fsharp#2105

@gusty
Copy link
Member

gusty commented Dec 29, 2016

Yes, I'm very happy of the improvements in the new Visual Studio, not just the speed ups I did, but that in conjunction with these changes you linked will create a way better experience for those wanting to contribute to this project.

I stopped adding important contributions because the developer experience was really bad, and here you need to test the code a lot, so if the compile-time is slow it can be really painful to get the code right. Same applies to intellisense.

@OnurGumus
Copy link
Author

Just wanted the add this issue still persists with the FSharp nightly releases.

@gusty
Copy link
Member

gusty commented Mar 20, 2017

@OnurGumus can you provide more information?

For instance the exact F# compiler version and can we measure the 'slowliness' somehow? Can you tell something like this code takes X milliseconds to compile?

So at least those three items will be nice to have, something like X compiler takes Y time to compile Z code.

@OnurGumus
Copy link
Author

@gusty Hey, after 4 years I just tried this again and still slow :)

@gusty
Copy link
Member

gusty commented Oct 27, 2021

Looks like the F# compiler didn't improve that much in this time.
Anyway, I was experimenting with a technique called type defunctionalization and managed to improve type inference in monad transformers, but it requires a substantial change in the library. I'm keeping it for v2.
Then in the compiler side of things there is a longstanding PR to take extensions methods into account for overload resolution which I think will reduce the level of hacking currently required to plumb everything together.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants