F# 结果单元测试在没有类型注释的情况下失败 - 在这种情况下我可以避免类型注释吗?

F# Result Unit Test Fails without type annotation - Can I avoid the type annotation in this case?

我正在做一些 F# 练习,任务是实现一个名为 clean 的函数,它接受一个字符串输入和 returns 一个已解析的数字。

let clean input = 
    input
    |> Seq.filter Char.IsDigit
    |> Seq.fold (fun acc cur ->
                    if acc = "" && cur = '1' then
                        acc
                    else acc + string cur) ""
    |> uint64
    |> Ok

这是我的临时解决方案。但是下面的单元测试失败了:

[<Fact>]
let ``Cleans the number`` () =
    let expected: Result<uint64,string> = Ok 2234567890UL
    let parsed = clean "(223) 456-7890"
    parsed |> should equal expected

失败:

FsUnit.Xunit+MatchException : Exception of type 'FsUnit.Xunit+MatchException' was thrown. Expected: Equals Ok 2234567890UL Actual: was Microsoft.FSharp.Core.FSharpResult2[System.UInt64,System.Object]<br> at FsUnit.Xunit.Assert.That.Static[a](a actual, IMatcher1 matcher)
at PhoneNumberTest.Cleans numbers with dots() in

所以我添加了一个类型注释:let clean input: Result<uint64,string> // function body is the same

我可以避免类型注释吗?第一个解决方案失败的技术原因是什么?

如果没有类型注释,您的 clean 函数将被推断为具有通用类型 seq<char> -> Result<uint64,'a>,因此在您需要类型注释的情况下。

编译器推断泛型类型的原因是您总是 return Result<'ok,'error> 类型的 OK 分支。

正如您的函数名称所暗示的,也许 Result 不是最合适的 return 类型?

这里的问题是编译器无法仅从成功值 Ok 2234567890UL 推断出您的 Result 在失败情况下的类型。当它不知道时,它只使用 obj,因此默认的推断类型是 Result<uint64, obj>。比较不同类型的对象总是 false.

这有点不幸,因为您正在比较两个值,因此它们必须具有相同的类型。提供 shouldequal 操作的 FsUnit 库不是严格类型化的,允许您比较不同类型的值。你可以这样写:

1 |> should equal "1"

有一个 library FsUnitTyped 实现了 FsUnit 的类型化版本。我没有这方面的经验,但看起来你可以使用它并写:

[<Fact>]
let ``Cleans the number`` () =
    let expected = Ok 2234567890UL
    let parsed = clean "(223) 456-7890"
    parsed |> shouldEqual expected

这将解决您的问题 - 编译器可以推断 expected 的类型必须与 parsed 的类型相同,因此它会自动使用正确的类型。