Почему использование оператора обратного конвейера устраняет ошибку компиляции?

Компилятор принимает следующую строку:

input |> Prop.forAll <| fun (a , b) -> add a b = add b a

Однако, когда я заменяю оператор обратного конвейера скобками, я получаю сообщение об ошибке:

input |> Prop.forAll ( fun (a , b) -> add a b = add b a )

Несоответствие типов. Ожидается Произвольное -> 'a, но задано ('b -> 'c) -> Свойство Тип 'Произвольный' не соответствует типу ''a -> 'b

Я не совсем уверен, что означает эта ошибка. Почему оператор обратного конвейера компилируется, а скобки — нет?

Приложение:

module Arithmetic

let add a b =
    a + b

open FsCheck
open FsCheck.Xunit

[<Property(MaxTest=1000, QuietOnSuccess=true)>]
let ``'a + 'b equals 'b + 'a`` () =

    // Declare generators per type required for function
    let intGenerator = Arb.generate<int>

    // Map previously declared generators to a composite generator 
    // to reflect all parameter types for function
    let compositeGenerator = (intGenerator , intGenerator) ||> Gen.map2(fun a b -> a , b)

    // Pull values from our composite generator
    let input = Arb.fromGen compositeGenerator

    // Apply values as input to function
    input |> Prop.forAll <| fun (a , b) -> add a b = add b a

person Scott Nimrod    schedule 17.08.2016    source источник
comment
Вы можете использовать Arb.generate<int*int> и полностью убить compositeGenerator.   -  person Dax Fohl    schedule 17.08.2016
comment
Вау, даже не подумал об этом... Спасибо!   -  person Scott Nimrod    schedule 17.08.2016


Ответы (3)


Функция Prop.forAll имеет тип Arbitrary<'a> -> ('a -> 'b) -> Property. Это означает, что первый аргумент должен быть Arbitrary, а следующий аргумент должен быть функцией ('a -> 'b).

Когда вы пишете input |> Prop.forAll (fun (a , b) -> add a b = add b a ), вы пытаетесь вызвать Prop.forAll с помощью (fun (a , b) -> add a b = add b a ), что компилятор пытается интерпретировать как частично примененную функцию.

Поскольку первым аргументом Prop.forAll является Arbitrary<'a>, компилятор пытается вывести функцию как Arbitrary, а это не так.

person Mark Seemann    schedule 17.08.2016

Во второй строке у вас есть аргументы в неправильном порядке.

Применение функции имеет наивысший приоритет, поэтому оно применяется первым, прежде чем все остальное. После этого применяются операторы <| и |>, и они имеют одинаковый приоритет, поэтому левый применяется первым, а правый — вторым. Итак, если вы рассмотрите эту строку:

x |> y <| z

Сначала вы применяете левую трубу и получаете:

(y x) <| z

А после применения правильной трубы вы получите:

y x z

Но если вы рассмотрите вторую строку, все наоборот:

x <| y (z)

После установки трубы:

y (z) x
person Fyodor Soikin    schedule 17.08.2016

input должен быть первым аргументом, поэтому просто

Prop.forAll input (fun (a , b) -> add a b = add b a)

Причина, по которой работает оператор конвейера, заключается в том, что прямой канал изменяет сходство в порядке синтаксического анализа.

input |> Prop.forAll (fun (a , b) -> add a b = add b a)
~
input |> (Prop.forAll (fun (a , b) -> add a b = add b a))
~
Prop.forAll (fun (a , b) -> add a b = add b a) input

который не компилируется. Обратная труба меняет его обратно.

input |> Prop.forAll <| fun (a , b) -> add a b = add b a
~
(input |> Prop.forAll) <| (fun (a , b) -> add a b = add b a)
~
(Prop.forAll input) (fun (a , b) -> add a b = add b a)
~
Prop.forAll input (fun (a , b) -> add a b = add b a)

что делает.

FWIW, все операторы канала в приведенном вами образце, похоже, больше запутывают, чем помогают. Конвейер обычно не рекомендуется для однострочников, за исключением случаев, когда это помогает вашему автозаполнению.

person Dax Fohl    schedule 17.08.2016