前提:我認爲管道操作員只是語法糖,因此x |> f
應該與f(x)
完全一樣。F#管道和功能應用程序之間的神祕區別
類似,我認爲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預計其通過f
匹配這反過來在推斷爲是因爲fileList
一個string
一個arg:obj
是文件名的列表。 但是以下不起作用
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
一致,因此我認爲它將統一,但這甚至不重要。爲什麼所有類型都有可能發生變化,因爲我再次相信這是純粹的句法替換?!
我還會注意到,根據錯誤消息判斷,'TestCaseData'函數可能不需要'obj',但''obj []',當你嘗試傳遞一個'obj'時工作,但當參數的類型還不知道時被推斷爲'obj []'。 –
@FyodorSoikin好點,並相應地更新。謝謝 –
這很有趣。因此,程序的有效性不僅僅取決於語法和(原則上)有效使用類型,而是實際依賴於當前實現的類型推斷機制。因此,給定的程序文本可能會變得有效/無效,這取決於微軟在編譯器中將來如何實現類型推斷?我發現,根據'f'和'x'的類型,'(|>)f x = f x'這樣的代數法則是有效的還是無效的。 –