3

The following line is accepted by the compiler:

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

However, when I replace the backwards pipline operator with parentheses, I receive an error:

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

Type mismatch. Expecting a Arbitrary -> 'a but given a ('b -> 'c) -> Property The type 'Arbitrary' does not match the type ''a -> 'b

I'm not quite sure what this error means. Why does the backwards pipeline operator compile but the parentheses doesn't?

Appendix:

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
Scott Nimrod
  • 11,206
  • 11
  • 54
  • 118

3 Answers3

7

On the second line, you have your arguments in the wrong order.

Function application has the highest precedence, so it's applied first, before everything else. The operators <| and |> get applied after that, and they have the same precedence, so the left one is applied first, and the right one is applied second. So if you consider this line:

x |> y <| z

First you apply the left pipe and get:

(y x) <| z

And after applying the right pipe, you get:

y x z

But if you consider the second line, it's the other way around:

x <| y (z)

After applying the pipe:

y (z) x
Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
3

input should be the first arg, so simply

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

The reason the pipeline operator works is that the forward pipe changes the affinity in the parse order.

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

which does not compile. The backwards pipe changes it back.

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)

which does.

FWIW all the pipe operators in the sample you've given seem to obfuscate things more than they help. Piping is usually not recommended for one-liners except in the case it helps your autocompletion.

Dax Fohl
  • 10,654
  • 6
  • 46
  • 90
2

The Prop.forAll function has the type Arbitrary<'a> -> ('a -> 'b) -> Property. This means that the first argument must be an Arbitrary, and the next argument a function ('a -> 'b).

When you write input |> Prop.forAll (fun (a , b) -> add a b = add b a ), you're attempting to invoke Prop.forAll with (fun (a , b) -> add a b = add b a ), which the compiler attempts to interpret as a partially applied function.

Since the first argument to Prop.forAll is Arbitrary<'a>, the compiler attempts to infer the function as an Arbitrary, which it isn't.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736