F # TaskBuilder: как вызвать исключение, не возвращая фиктивное значение для соответствия ограничениям компилятора? - PullRequest
2 голосов
/ 06 февраля 2020

В F # TaskBuilder task я пытаюсь перехватить исключение и повторно выдать его как завернутое исключение:

open System.Threading.Tasks

open FSharp.Control.Tasks.V2


exception FakeDapperException of int

exception FakePersistenceException of string * exn


module FakeDapper =
    let executeWrongQueryAsync (sql: string) =
        task {
            raise (FakeDapperException(0))
            return 42
        }

module FakePersistenceLayer =
    let doSomeImportantDbRelatedStuffAsync (sql: string) =
        task {
            try
                return! FakeDapper.executeWrongQueryAsync sql
            with e ->
                FakePersistenceException(sql, e) |> raise
                return Unchecked.defaultof<int>
        }

module Task =
    let GetResult (task: Task<'T>) =
        task.GetAwaiter().GetResult()


[<EntryPoint>]
let main _ =
    let result =
        FakePersistenceLayer.doSomeImportantDbRelatedStuffAsync "this is some pretty sql stuff"
        |> Task.GetResult
    0

Приведенный выше код работает, как и ожидалось, однако, когда я впервые реализовал, я хотел иметь что-то вроде ниже:

let doSomeImportantDbRelatedStuffAsync (sql: string) =
    task {
        try
            return! FakeDapper.executeWrongQueryAsync sql
        with e ->
            FakePersistenceException(sql, e) |> raise
    }

, который не может быть скомпилирован:

Program.fs(23, 17): [FS0001] Type mismatch. Expecting a 'FSharp.Control.Tasks.TaskBuilder.Step<int>' but given a 'FSharp.Control.Tasks.TaskBuilder.Step<unit>'. 
The type 'int' does not match the type 'unit'

Это связано с тем, как работает task CE, но мне интересно, есть ли способ не возвращать бесполезное значение (например, return Unchecked.defaultof<int>), чтобы компилятор не кричал на меня.

1 Ответ

3 голосов
/ 07 февраля 2020

Проблема в том, что raise не знает, какой тип выводить из-за нахождения в task CE. Так как первая ветвь try имеет return, последняя ветвь также должна иметь одну (это return - то, куда приходит вывод типа)

let doSomeImportantDbRelatedStuffAsync (sql: string) =
    task {
        try
            return! FakeDapper.executeWrongQueryAsync sql
        with e ->
            return FakePersistenceException(sql, e) |> raise
    }

Это должно сделать вывод типа счастливым, как raise теперь выведет тип task CE.

...