dotnet / dotnet/fsharp

Type inference on task state machine could be improved

Open
#14,415 0 comments 3 reactions 0 assignees View on GitHub
Area-Compiler-StateMachines Feature Request
Dominant language
F#
Stars
4.3k
Forks
876
Avg merge
4d 22h
Merged PRs (30d)
144

Description

Consider this F# file (self-contained for convenience, originally taken [from this snippet](https://github.com/codingteam/emulsion/blob/3a3b9c9c776aeab422d90758b0a862803df9d3e3/Emulsion.Web/ContentController.fs#L32-L64) of an ASP.NET Core app).

Target is `net7.0`, it can be compiled using .NET SDK 7.

```fsharp
open System.Threading.Tasks

type IActionResult =
abstract member Stub: unit -> unit

type ActionResult() =
interface IActionResult with
member _.Stub() = ()

type StatusCodeResult() =
inherit ActionResult()

type BadRequestResult() =
inherit StatusCodeResult()

type NotFoundResult() =
inherit StatusCodeResult()

type UnprocessableEntityResult() =
inherit StatusCodeResult()

type FileResult() =
inherit ActionResult()

type RedirectResult() =
inherit StatusCodeResult()

type FileStreamResult() =
inherit FileResult()

type ControllerBase() =
member _.BadRequest(): BadRequestResult =
failwith ""

member _.NotFound(): NotFoundResult =
failwith ""

type TestClass() =
inherit ControllerBase()

member this.Task(): Task = task {
match Some "xxx" with
| None ->
return this.BadRequest()
| Some contentId ->
match! Task.FromResult(Some 123) with
| None ->
return this.NotFound() :> IActionResult
| Some content ->
match Some "xx" with
| None ->
match "xxx" with
| "" -> return UnprocessableEntityResult()
| _ ->
return RedirectResult()
| Some cache ->
match! Task.FromResult(Some "xx") with
| None ->
return this.NotFound() :> IActionResult
| Some fileInfo ->
match! Task.FromResult(Some 1) with
| None ->
return this.NotFound() :> IActionResult
| Some stream ->
match "aa" with
| "application/octet-stream" -> return FileStreamResult()
| _ -> return FileStreamResult()
}
```

In this snippet, every `:> IActionResult` is required; the code refuses to compile without them.

Using `` though, it could be as well compiled without these explicit casts:
```fsharp
open System.Threading.Tasks

open FSharp.Control.Tasks

type IActionResult =
abstract member Stub: unit -> unit

type ActionResult() =
interface IActionResult with
member _.Stub() = ()

type StatusCodeResult() =
inherit ActionResult()

type BadRequestResult() =
inherit StatusCodeResult()

type NotFoundResult() =
inherit StatusCodeResult()

type UnprocessableEntityResult() =
inherit StatusCodeResult()

type FileResult() =
inherit ActionResult()

type RedirectResult() =
inherit StatusCodeResult()

type FileStreamResult() =
inherit FileResult()

type ControllerBase() =
member _.BadRequest(): BadRequestResult =
failwith ""

member _.NotFound(): NotFoundResult =
failwith ""

type TestClass() =
inherit ControllerBase()

member this.Task(): Task = task {
match Some "xxx" with
| None ->
return this.BadRequest()
| Some contentId ->
match! Task.FromResult(Some 123) with
| None ->
return this.NotFound()
| Some content ->
match Some "xx" with
| None ->
match "xxx" with
| "" -> return UnprocessableEntityResult()
| _ ->
return RedirectResult()
| Some cache ->
match! Task.FromResult(Some "xx") with
| None ->
return this.NotFound()
| Some fileInfo ->
match! Task.FromResult(Some 1) with
| None ->
return this.NotFound()
| Some stream ->
match "aa" with
| "application/octet-stream" -> return FileStreamResult()
| _ -> return FileStreamResult()
}
```

I believe that this is something that could be improved in the compiler or in the default `task` builder implementation.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.