F# 管道和函数应用程序之间的神秘差异
F# mysterious differences between piping and function application
前提:我以为管道运算符只不过是语法糖,因此x |> f
应该和f(x)
完全一样。
类似地,我认为f (fun x -> foo)
等同于let g = fun x -> foo; f g
但显然有一些我不明白的差异。
示例 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
相匹配,后者又被推断为 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 是第一个 lexical 参数。
然后在管道表达式中使用 fileList 是字符串列表的信息。要知道 string
是否是 f 的合法类型,使用 TestCaseData 的签名。如评论所述,根据错误消息,TestCaseData 可能接受 [<Params>] obj []
使单个字符串参数有效。
在第二个版本中,除了 TestCaseData 的签名外,在确定 f 的类型时没有可使用的信息,因此 f
被推断为 obj []
类型
另一个例子也是如此。恰恰相反。提取该函数会删除它应该属于 TestDelegate
类型的信息。
在词汇点,唯一可用的信息是它是类型 unit->unit
的函数。
当在需要 TestDelegate
的程序点使用该功能时。类型推断测试函数是否可以用作 TestDelegate
,如果可以,则推断类型为 TestDelegate
前提:我以为管道运算符只不过是语法糖,因此x |> f
应该和f(x)
完全一样。
类似地,我认为f (fun x -> foo)
等同于let g = fun x -> foo; f g
但显然有一些我不明白的差异。
示例 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
相匹配,后者又被推断为 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 是第一个 lexical 参数。
然后在管道表达式中使用 fileList 是字符串列表的信息。要知道 string
是否是 f 的合法类型,使用 TestCaseData 的签名。如评论所述,根据错误消息,TestCaseData 可能接受 [<Params>] obj []
使单个字符串参数有效。
在第二个版本中,除了 TestCaseData 的签名外,在确定 f 的类型时没有可使用的信息,因此 f
被推断为 obj []
另一个例子也是如此。恰恰相反。提取该函数会删除它应该属于 TestDelegate
类型的信息。
在词汇点,唯一可用的信息是它是类型 unit->unit
的函数。
当在需要 TestDelegate
的程序点使用该功能时。类型推断测试函数是否可以用作 TestDelegate
,如果可以,则推断类型为 TestDelegate