1

I'm writing a quick DB perf test, and chose F# so I can get more practice.

I've created a method, measureSelectTimes, which has the signature Guid list * Guid list -> IDbCommand -> TimeSpan * TimeSpan.

Then, I call it:

let runTests () =
    let sqlCeConn : IDbConnection = initSqlCe() :> IDbConnection
    let sqlServerConn : IDbConnection = initSqlServer() :> IDbConnection
    let dbsToTest = [ sqlCeConn; sqlServerConn ]
    let cmds : seq<IDbCommand> = dbsToTest |> Seq.map initSchema
    let ids : seq<Guid list * Guid list> = cmds |> Seq.map loadData
    let input = Seq.zip ids cmds
    let results = input |> Seq.map (fun i -> measureSelectTimes (fst i) (snd i))
    // ...

I've annotated explicitly with types to clarify.

What I can't figure out is how to call measureSelectTimes without the lambda. I'd like to partially apply the ids to it like this: ids |> Seq.map measureSelectTimes but then I don't know what to do with the resulting partially applied functions to then map onto the cmds. What's the syntax for this?

duplode
  • 33,731
  • 7
  • 79
  • 150
codekaizen
  • 26,990
  • 7
  • 84
  • 140
  • 1
    BTW, you can write your lambda as `(fun (id,cmd)->measureSelectTimes id cmd)`. (It's unrelated to the question, though) – Dmitry May 25 '11 at 18:51
  • @Dmitry - thanks, good stuff. This is why I posted the question, I need to expand my F# vocab. – codekaizen May 25 '11 at 19:31

3 Answers3

9

You can use Seq.map2:

Seq.map2 measureSelectTimes ids cmds

Or

(ids, cmds) ||> Seq.map2 measureSelectTimes
kvb
  • 54,864
  • 2
  • 91
  • 133
  • 1
    `let partial = Seq.map2 measureSelectTimes ids`, indeed. It's so obvious and easy... when you see the solution. But before I saw it it did'n even crossed my mind. :-( +1 – Dmitry May 25 '11 at 18:42
  • This is what I was looking for; I didn't see how Seq.map2 worked, but this cleared it up. Thanks also @Dmitry for how to express the result as a partial to apply, which is what I was ultimately thinking of. +1 – codekaizen May 25 '11 at 19:54
  • 4
    +1 for teaching me a new operator - where did "||>" come from!? It's not on the [MSDN](http://msdn.microsoft.com/en-us/library/dd233228.aspx) page. – Samuel May 25 '11 at 23:48
3

Your measureSelectTimes function takes two arguments as separate arguments, but you instead need a function that takes them as a tuple. One option is to just change the function to take a tuple (if it is logical for the arguments to be tupled).

Alternative, you can write a cobinator that turns a function taking two arguments into a function taking tuple. This is usually called uncurry and it exists in some functional language:

let uncurry f (a, b) = f a b

Then you can write:

input |> Seq.map (uncurry measureSelectTimes)

This looks okay for a simple use like this, but I think that using combinators too much in F# is not a good idea as it makes code difficult to read for less experienced functional programmers. I would probably write something like this (because I find that more readable):

[ for (time1, time2) in input -> measureSelectTimes time1 time2 ]
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • Thanks for the answer and the 2 new tools of using an uncurry function and a list comprehension to solve it. Also, thanks for the book. – codekaizen May 25 '11 at 19:56
0

One approach is to change the signature of measureSelectTimes to

(Guid list * Guid list) * IDbCommand -> TimeSpan * TimeSpan

Then you can change the map call to

let results = input |> Seq.map measureSelectTimes
// or
let results = Seq.map measureSelectTimes input
ildjarn
  • 62,044
  • 9
  • 127
  • 211