F # signature match

I run into difficulties with F # in numerous scenarios. I guess I don't understand some fundamental concepts. I hope someone can trace my reasoning and figure out (maybe many) things that I am missing.

Let's say I am using Xunit. What I would like to do is two lists, apply the method in Assert.Equal

pairs. For example:

Open Xunit
let test1 = [1;2;3]
let test2 = [1;2;4]
List.map2 Assert.Equal test1 test2

      

The compiler complains that the function Equal

does not take one parameter. As far as I can tell, shouldn't you map2

provide it with 2 parameters?

As a sanity check, I use the following code in f # immediate:

let doequal = fun x y -> printf "result: %b\n" (x = y)
let test1 = [1;2;3]
let test2 = [1;2;4]
List.map2 doequal test1 test2;;

      

It seems identical. doequal

is a lambda that takes two common parameters and returns unit . List.map2

passes each argument pairwise to a lambda and I get exactly what I expected as output:

result: true
result: true
result: false

      

So what gives? The source shows Xunit.Equal

has a signature public static void Equal<T>(T expected, T actual)

. Why won't my parameters appear right above the method signature?

EDIT ONE I thought that two variables x and y versus tuple (x, y) can build and deconstruct interchangeably. So I tried two options and got different results. It seems that the second can be further than the first.

List.map2 Assert.Equal(test1, test2)

The compiler now complains that "sequential arguments must be separated by spaces or alternating"

List.map2(Assert.Equal(test1, test2))

The compiler now complains that "a unique overloading method cannot be defined ... Type annotation may be required"

+3


source to share


4 answers


I think part of the problem has to do with mixing techniques (OO style) and functions (FP style).

  • FP style functions have multiple parameters, separated by spaces.
  • OO style methods have parameters and parameters separated by commas.
  • Methods in other .NET libraries are always named using the "tuple" syntax (actually subtly different from tuples) and the tuple counts as one of the parameters.

The F # compiler tries to handle both approaches, but sometimes needs some help.

One approach is to "wrap" the OO method with an FP function.

// wrap method call with function
let assertEqual x y = Assert.Equal(x,y)

// all FP-style functions
List.map2 assertEqual test1 test2

      

Unless you are creating a helper function, you often need to convert multiple function parameters to a single tuple when calling the "inline" method with a lambda:

List.map2 (fun x y -> Assert.Equal(x,y)) test1 test2

      

When you mix methods and functions on the same line, you often get the "Consecutive arguments must be separated" error.

printfn "%s" "hello".ToUpper()  
// Error: Successive arguments should be separated 
// by spaces or tupled

      



This suggests that the compiler is having problems and need help!

You can solve this problem by using additional partners during the method call:

printfn "%s" ("hello".ToUpper())  // ok

      

Or sometimes with a return pipe:

printfn "%s" <| "hello".ToUpper() // ok

      

A workaround is often worth doing so that you can change the parameters to make it more suitable for partial application:

// wrap method call with function AND swap params
let contains searchFor (s:string) = s.Contains(searchFor)

// all FP-style functions
["a"; "b"; "c"]
|> List.filter (contains "a")

      

Note that on the last line, I had to use parens to take precedence contains "a"

overList.filter

+6


source


public static void Equal<T>(T expected, T actual)

      

not takes two parameters - it takes one parameter, which is a tuple with two elements (T expected, T actual)

.



Try this instead:

List.map2 Assert.Equal(test1, test2)

      

+5


source


All this is in the type of signatures.

The signature for Assert.Equals

is something like strings 'a * 'a -> unit

. List.map2

expects a 'a -> 'b -> 'c

.

They just don't fit together.

List.map2 (fun x y -> Assert.Equal(x,y)) test1 test2

- works because the lambda wrapper Equals

has the expected signature.

List.zip test1 test2 |> List.map Assert.Equal

- works because you now have a single list of tuples, and since it List.map

wants a function 'a -> 'b

(where 'a

is a tuple Assert.Equal

now ), is fair game now.

It is simply not true that two values ​​and a tuple are implicitly interchangeable. At least not in the same way as F # this language, or basic IL representation. You may think that when you call F # code, for example with a C # - a 'a -> 'b -> 'c

function 'a * 'b -> 'c

, it is indeed called the same syntactically as a function , but this is more the exception than the rule.

+2


source


As per its signature Xunit.Assert.Equal()

takes one parameter 2 of the tuple parameter

+1


source







All Articles