F# 管道和函数应用程序之间的神秘差异

标签 f# type-inference piping

前提:我认为管道运算符只不过是语法糖,因此 x |> f 应该与 f(x) 完全相同。

类似地,我认为 f (fun x -> foo) 等同于 let g = fun x -> foo; fg

但显然存在我不理解的差异。

示例 1:

static member contents = 
    let files = Directory.EnumerateFiles (__SOURCE_DIRECTORY__+ @"\foo\bar.txt")
    let fileList = List.ofSeq files
    fileList |> List.map (fun f -> TestCaseData(f).SetName(""))

这很好用:TestCaseData 需要一个 arg:obj,它与 f 相匹配,而 f 又被推断为一个 string,因为 fileList 是一个文件名列表。 但是以下不起作用

static member contents = 
    let files = Directory.EnumerateFiles (__SOURCE_DIRECTORY__+ @"\foo\bar.txt")
    let fileList = List.ofSeq files
    List.map (fun f -> TestCaseData(f).SetName("")) fileList

除了最后一行没有改变。突然 f 被推断为 obj [] 并且 TestCaseData 需要类型为 obj [] 的参数,因此我得到一个错误

Error   1   Type mismatch. Expecting a obj [] list but given a string list    
The type 'obj []' does not match the type 'string'

我本以为这两个片段在生成正确代码方面是等价的,但只有第一个片段可以?!

示例 2:

[<TestCase("nonsense", TestName="Nonsense")>]
member x.InvalidInputs str =
    let lexbuf = Microsoft.FSharp.Text.Lexing.LexBuffer<char>.FromString(str)
    Assert.Throws<FatalError> (fun () -> ParsePkg.parse "Dummy path" lexbuf |> ignore)
    |> ignore

以上一切正常。

[<TestCase("nonsense", TestName="Nonsense")>]
member x.InvalidInputs str =
    let lexbuf = Microsoft.FSharp.Text.Lexing.LexBuffer<char>.FromString(str)
    let ff = fun () -> ParsePkg.parse "Dummy path" lexbuf |> ignore
    Assert.Throws<FatalError> (ff)
    |> ignore

如您所见,我所做的只是通过首先定义 let ff = ...(例如,出于可读性原因)来提取断言的参数,然后编译器突然指向 (ff) 争论和提示:

Error   2   This expression was expected to have type TestDelegate but here has type unit -> unit

TestDelegate 是我在这里使用的一种 NUnit,它恰好与 unit->unit 重合,所以我假设它无论如何都是统一的,但这并不重要。为什么类型有可能发生变化,因为我再次相信已经完成了纯粹的句法替换?!

最佳答案

类型推断是从上到下按顺序进行的。所以在第一种情况下,fileList 是第一个词法 参数。 然后在管道表达式中使用 fileList 是字符串列表的信息。要知道是否string是使用 TestCaseData 签名的 f 的合法类型。正如所评论的,基于错误消息 TestCaseData 可能接受 [<Params>] obj []使单个字符串参数有效。

在第二个版本中,除了 TestCaseData 的签名外,在确定 f 的类型时没有任何信息可供使用,因此 f被推断为 obj [] 类型

另一个例子也是如此。恰恰相反。拉出该函数会删除它应该属于 TestDelegate 类型的信息。 .

在词法方面,唯一可用的信息是它是一个 unit->unit 类型的函数。 .

当函数在程序点使用时 TestDelegate是必须的。类型推断测试函数是否可以用作 TestDelegate如果是,则推断类型为 TestDelegate

关于F# 管道和函数应用程序之间的神秘差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35777478/

相关文章:

f# - F# 有循环退出语句吗?

F# Seq.sortBy 降序

c - 给定一组 UNIX 命令的管道

c# - 为什么 C# 编译器不自动推断此代码中的类型?

node.js - Node.js Stream API 的意外行为

r - 使用链接命令循环打印 ggplot

F# 使用 Seq.map 创建更小的元组

F# 选项...它们真的能防止空引用异常吗

java - 返回参数类型受泛型类型参数约束的 lambda 函数

java - 我可以在 Java 中推断子类型吗?