The previous post demonstrates my thought process for solving problems, enhanced with the aid of the F# Interactive (FSI) Window. The programming exercise I used was presented at August’s F# SIG in Cleveland. The exercise was to recursively act on a directory with different operations: get the total size of all the files, delete all the files, move all the files, etc. In the previous post, I skipped over the “move files” part of the problem because it introduced a new concept that I felt would have detracted from the focus of the post. This post completes the exercise, introducing partial function application to solve .
Here’s the higher-order function that we came up with last time:
|
|
The fileOp
argument takes the function that will perform an operation on each file. Here’s an example:
|
|
The signature for fileOp
is (seq<string> -> 'a)
. The parentheses tell us that the argument is a function. The type after the last arrow is the return type. 'a
means that the function will return a generic type. For example, getSize
returns int64
, and delete
returns unit
(kind of like void
in C#). The leftmost part of the signature tells us the types of the arguments to the function. fileOp
takes one argument: a sequence of strings.
So here’s the problem with copyFiles
, and why I didn’t include it in the last post: in addition to the files to copy, we need to also provide a destination directory for them, and the root path of the source directory1. This makes the function signature different from what actOnFiles
expects. Fortunately, functional languages (including F#) have an eloquent way to solve this problem!
The technique is called Partial Function Application2. The idea is that you create a new function by “fixing” one or more of the first arguments of another function. Here’s a classic example:
|
|
You can see how we created a new function, add5
, by fixing the value 5 as the first argument to add
.
So let’s get a look at the copyFiles
function:
|
|
This function has the signature string -> string -> seq<string> -> unit
, but we need for it to look like seq<string> -> 'a
. Here’s how we can do that using Partial Function Application:
|
|
What we are doing is supplying the first two arguments to create a function with the signature we need for passing into actOnFiles
. To put it another way, we are taking a function with the signature string -> string -> seq<string> -> 'a
, pre-wiring the two string parameters so that we don’t need to supply them, leaving us with a function that has the signature seq<string> -> 'a
. Perfect!
In the above example, I explicitly bound the new function to an identifier. This isn’t necessary, as the following code shows:
|
|
Note the use of the parentheses and the absence of the files
argument, which was necessary earlier because F# couldn’t infer the type correctly3.
That’s it! Here’s the code for the exercise in its entirety:
|
|
1 The source argument seemed to be a necessary evil so that I knew the "root" of where the copy was to take place. You will see a subtle form of duplication in the overall code which I'm not crazy about. I welcome suggestions. 2 Recently, I have heard that Partial Function Application and Currying are two different terms. This [article](http://mikehadlow.blogspot.com/2010/04/currying-vs-partial-function.html) may shed some light. I said "may"... 3 Without the files argument, I was getting this compiler error: "Either make the arguments to 'copyPartial' explicit or, if you do not intend for it to be generic, add a type annotation." Help to resolve this would be appreciated.