F#管道和功能应用之间的神秘差异

Fri*_*etz 3 f# type-inference piping

前提:我认为管道操作员只不过是语法糖,因此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(""))
Run Code Online (Sandbox Code Playgroud)

这工作得很好: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
Run Code Online (Sandbox Code Playgroud)

除了最后一行之外什么都没有改变.突然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'
Run Code Online (Sandbox Code Playgroud)

我会认为这两个片段在产生正确的代码中是等效的,但只有第一个呢?!

例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
Run Code Online (Sandbox Code Playgroud)

以上一切正常.

[<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
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我所做的就是通过首先定义let ff = ...(出于可读性的原因,比如说)来提取断言的参数,然后编译器突然指向(ff)参数并抱怨:

Error   2   This expression was expected to have type TestDelegate but here has type unit -> unit
Run Code Online (Sandbox Code Playgroud)

TestDelegate是我在这里使用的一种NUnit,它恰好与之相符,unit->unit所以我认为它无论如何都会统一,但这甚至都不重要.为什么这个类型可能会发生变化,因为我再次认为它已经完成了一个纯粹的语法替换?!

Run*_* FS 7

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

在第二个版本中,除了TestCaseData的签名之外,在确定f的类型时没有信息f可用,因此推断它是类型obj []

在另一个例子中也是如此.反过来说.拉出该函数会删除它应该是类型的信息TestDelegate.

在词汇点,唯一可用的信息是它是类型的函数unit->unit.

在需要a的程序点使用该函数TestDelegate时.类型推断测试该函数是否可以用作a TestDelegate,如果是,则推断出类型TestDelegate

  • 我还要注意,从错误消息判断,`TestCaseData`函数可能不是`obj`,而是`[<Params>] obj []`,当你试图传递一个`obj`时,它会起作用,但是当参数的类型尚不知道时,它被推断为`obj []`. (3认同)