Search code examples
.netf#task-parallel-library

F# - Convert Task<unit> to plain Task


I'm using Giraffe to create native .NET Tasks with Async workflows since I need interoperability with the Discord.NET library. I've got an event that takes a Task, but the event handler that I'm writing returns a Task<unit> and for some reason the type checker isn't smart enough to realize that it's the same thing. Is there a way to convert the Task<unit> to a straight up Task?

Here's my code:

let messageReceived (msg : SocketMessage) =
    task {
        let author = msg.Author.Username
        let text = msg.Content
        match msg.Author.Username with
            | "Monika" -> ()
            | usr ->
                if text.ToLower().StartsWith "<@407367655830585344> say" then
                    text.Substring 26 |> msg.Channel.SendMessageAsync |> ignore
                else if text.ToLower().StartsWith "<@407367655830585344>" then
                    let response = (Lines.VoiceLines |> Extras.randomElement).Replace("[player]", author)
                    msg.Channel.SendMessageAsync response |> ignore
                else if text.ToLower().StartsWith "delete" then
                    text.Substring 7 + ".chr deleted" |> msg.Channel.SendMessageAsync |> ignore
                else if Extras.onein 10 then
                    let response = (Lines.VoiceLines |> Extras.randomElement).Replace("[player]", author)
                    msg.Channel.SendMessageAsync response |> ignore
    }

_client.add_MessageReceived (fun m -> messageReceived m)

The last line generates an error, saying that the MessageReceived event needs a function with the signature SocketMessage -> Task:

error FS0001: This expression was expected to have type 'Task' but here has type 'Task<unit>'


Solution

  • Task is the base class of Task<T>, so you should be able to upcast Task<unit> to Task with :> operator. Try something like this:

    (fun m -> messageReceived m :> Task)
    

    I think this minimal example reproduces the issue:

    // int -> Task<unit>
    let sleepTaskT (m : int) = Async.Sleep m |> Async.StartAsTask
    // (int -> Task) -> Task
    let foo (f : int -> Task) = f 1000
    // "This expression was expected to have type 'Task' but here has type 'Task<unit>'"
    foo (fun m -> sleepTaskT m)
    // this works by explicitly upcasting sleepTaskT's Task<unit> to Task
    foo (fun m -> sleepTaskT m :> Task)