Compilation gets much slower when using many computation expressions
- Dominant language
- F#
- Stars
- 4.3k
- Forks
- 876
- Avg merge
- 4d 11h
- Merged PRs (30d)
- 131
Description
In a big Fable project, after a big refactoring to introduce a convenience computation expression, the project started to take much longer to compile. The overhead came from the type checking phase of FSharp.Compiler.Service (it also takes a long time when running `dotnet build`). The computation expression is a convenience to generate React fragments out of React elements. The CE accepts single React Elements as well as sequences and optional elements. The code is like this:
```fsharp
type ElementBuilder() =
member _.Zero () : ReactElements =
[]
member _.Yield (e: ReactElement) : ReactElements =
[e]
member _.Yield (maybeE: Option) : ReactElements =
match maybeE with
| None -> []
| Some e -> [e]
member _.Yield (maybeEs: Option>) : ReactElements =
match maybeEs with
| None -> []
| Some es -> es
member _.Yield (es: List) : ReactElements =
es
member _.Yield (es: seq) : ReactElements =
Seq.toList es
member _.Yield (ess: list>) : ReactElements =
List.flatten ess
member _.Yield (ess: seq>) : ReactElements =
ess |> Seq.collect id |> Seq.toList
member _.Combine (e: ReactElement, es: ReactElements) : ReactElements =
e :: es
member _.Combine (moreEs: List, es: ReactElements) : ReactElements =
moreEs @ es
member _.Combine (moreEs: seq, es: ReactElements) : ReactElements =
(Seq.toList moreEs) @ es
member _.Combine (moreEs: List>, f: unit -> ReactElements) : ReactElements =
(List.flatten moreEs) @ (f())
member _.Combine (e: ReactElement, f: unit -> ReactElements) : ReactElements =
e :: (f())
member _.Combine (moreEs: List, f: unit -> ReactElements) : ReactElements =
moreEs @ (f())
member _.Combine (moreEs: seq, f: unit -> ReactElements) : ReactElements =
(Seq.toList moreEs) @ (f())
member _.Combine (moreEs: List>, es: ReactElements) : ReactElements =
(List.flatten moreEs) @ es
member _.Delay (expr: unit -> ReactElements) : unit -> ReactElements = expr
member _.Run (f: unit -> ReactElements) : ReactElement =
let elements = f()
Fable.React.Helpers.fragment [] elements
let element = ElementBuilder()
```
There's a similar CE `ElementsBuilder` that works the same but returns `ReactElement list` instead of the fragment.
Users tried changing the CE in different ways, but the only thing that worked was to remove the CE in most of the files. It is still used in some of them but compilation times are mostly back to normal.
The most interesting part is I tried profiling this, and the result is most of the time is spent in the function `FSharp.Compiler.NameResolution.ResolveLongIdentAsModuleOrNamespaceThen`. This makes me wonder if the CE is not responsible for the slow compilation but instead it causes a problematic name resolution directly. Both `ElementBuilder` and `element` are in the same module decorated with `AutoOpen`.

Any hints to improve the performance of the compilation?
Contributor guide
Assessment
This issue has not been assessed yet.