2016-03-03 4 views
3

Помещение: Я думал, что оператор трубопровода - это ничего, кроме синтаксического сахара, поэтому 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 ожидает arg:obj который подобран f в свою очередь, в Inferred быть 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, поэтому я предполагаю, что он будет унифицирован в любом случае, но это даже не имеет значения. Почему вообще возможно, что тип меняется, так как снова я считаю, что сделал чисто синтаксическую замену ?!

ответ

7

тип выводы делается последовательно сверху вниз. Итак, в первом случае fileList является первым лексическим аргументом. Информация о том, что файлList является списком строк, затем используется в выражении трубопровода. Чтобы узнать, является ли string юридическим типом для f, используется подпись TestCaseData. Как прокомментировано, на основе сообщения об ошибке TestCaseData, вероятно, принимает значение [<Params>] obj [], делая допустимым один аргумент строки.

Во втором варианте нет никакой информации для использования при определении типа F, отличных от подписи TestCaseData и для этого f выводится как тип obj []

Похожие верно в другом примере. Просто наоборот. Выключение функции удаляет информацию о том, что она должна быть типа TestDelegate.

В лексической точке доступна только информация о том, что это функция типа unit->unit.

Если функция используется в программной точке, где требуется TestDelegate. Вывод типа проверяет, может ли функция использоваться как TestDelegate, и если да, то вводится тип TestDelegate

+3

Я также хотел бы отметить, что, судя по сообщению об ошибке, функция 'TestCaseData', вероятно, принимает не' obj', но '[] obj []', который работает, когда вы пытаетесь передать ему одиночный 'obj', но получает вывод как' obj [] ', когда тип аргумента еще не известен. –

+0

@FyodorSoikin хорошая точка и обновляется соответственно. спасибо –

+0

Это интересно. Таким образом, действительность программы зависит не только от синтаксиса и (в принципе) от действительного использования типов, но и от существующего в настоящее время механизма вывода типа. Таким образом, данный текст программы может стать действительным/недействительным в зависимости от будущей реализации Microsoft в отношении вывода типа в компиляторе? Я нахожу очень тревожным, что алгебраические законы, такие как '(|>) f x = f x', действительны или недействительны в зависимости от того, какие типы' f' и 'x' имеют. –