Почему использование оператора обратного конвейера разрешает ошибку компиляции?
Следующая строка принята компилятором:
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, но с учетом свойства 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
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
что это не так.
Во второй строке у вас есть аргументы в неправильном порядке.
Приложение-функция имеет наивысший приоритет, поэтому оно применяется первым, а не всем остальным. Операторы <|
а также |>
применяются после этого, и они имеют одинаковый приоритет, поэтому левый применяется первым, а правый - вторым. Итак, если вы считаете эту строку:
x |> y <| z
Сначала вы применяете левую трубу и получаете:
(y x) <| z
И после применения правильной трубы, вы получите:
y x z
Но если рассмотреть вторую строку, все наоборот:
x <| y (z)
После нанесения трубы:
y (z) x
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, кажется, что все операторы канала в приведенном вами примере запутывают вещи больше, чем помогают. Трубопроводы обычно не рекомендуются для однострочников, за исключением случаев, когда это помогает вашему автозаполнению.