StevePoling
StevePoling

Reputation: 339

How can I follow F# Lint's suggestion to use `id`

I am comparing two lists of thangs. Since I'm more familiar with Linq than F#, I did this: let r1 = (rows1.Zip (rows2, fun r1 r2 -> rowComparer r1 r2)) .All (fun f -> f)

This raises two complaints from the F# linter. Lint: If `rowComparer` has no mutable arguments partially applied then the lambda can be removed. Lint: `fun x -> x` might be able to be refactored into `id`.

Of these, I could understand the latter, and tried this: let r1 = (rows1.Zip (rows2, fun r1 r2 -> rowComparer r1 r2)) .All id But this made the F# compiler complain:

This expression was expected to have type 'System.Func<bool,bool>'
but here has type ''a -> 'a'

Can someone say how this code can be more righteous?

Upvotes: 1

Views: 232

Answers (2)

kaefer
kaefer

Reputation: 5751

You can supply the standard function id as an argument if you create an instance of System.Func with the correct number of generic type parameters from it. When employing a lambda expression, the F# compiler does that for you.

open System.Linq
let foo rowComparer (rows1 : seq<_>) (rows2 : seq<_>) =
    (rows1.Zip (rows2, fun r1 r2 -> rowComparer r1 r2)).All(System.Func<_,_>(id))
// val foo :
//   rowComparer:('a -> 'b -> bool) -> rows1:seq<'a> -> rows2:seq<'b> -> bool

Upvotes: 2

Aaron M. Eshbach
Aaron M. Eshbach

Reputation: 6510

I would suggest using the F# List or Seq modules instead of LINQ methods. Then you'll be able to use F# types like 'a -> 'a instead of System.Func<'a, 'a>, and you can pass id to the forAll function. If you could post a complete example, it would be easier to give you a complete answer, but I think something like this would be roughly equivalent to what you're doing with LINQ:

let compare (rowComparer: ('a * 'a) -> bool) rows = 
    Seq.zip rows >> Seq.map rowComparer >> Seq.forall id

This creates a function that takes two sequences and compares each value in the first to the corresponding value in the second, generating a sequence of booleans. It then returns true if all of the values in the sequence are true, otherwise it returns false. This is achieved using function composition and partial application to build a new function with the required signature.

You can then partially apply a row comparer function to create a specialized compare function for each of your scenarios, as follows:

let compareEqual = compare (fun (a,b) -> a = b) 
compareEqual [0; 1; 2] [0; 1; 2] // true
compareEqual [0; 1; 2] [2; 1; 2] // false

Upvotes: 4

Related Questions