前提:我认为管道操作符只是语法糖,因此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'
类型的参数,因此我收到错误
[<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
我原以为两个片段在产生正确的代码时是相同的,但只有第一个呢?
示例2:
[<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
参数并抱怨:< / p>
unit->unit
TestDelegate是我在这里使用的一种NUnit,它碰巧与{{1}}重合,所以我认为它无论如何都会统一,但这甚至都不重要。为什么这个类型有可能发生变化,因为我再次认为它已经完成了纯粹的语法替换?!
答案 0 :(得分:7)
类型推断从上到下顺序完成。所以在第一种情况下,fileList是第一个词汇参数。
然后,在管道表达式中使用fileList是字符串列表的信息。要知道string
是否是f的合法类型,则使用TestCaseData的签名。根据错误消息,TestCaseData可能会接受[<Params>] obj []
使单个字符串参数有效。
在第二个版本中,在确定除TestCaseData的签名之外的f的类型时没有信息可用,因此f
被推断为类型obj []
在另一个例子中也是如此。反过来说。拉出该函数会删除它应该是TestDelegate
类型的信息。
在词汇点,唯一可用的信息是它是unit->unit
类型的函数。
在需要TestDelegate
的程序点使用该功能时。类型推断测试该函数是否可以用作TestDelegate
,如果是,则推断类型为TestDelegate