I'm just starting with F# so I thought I would try some simple tasks.
This lists the full paths to the xml files in a directory:
System.IO.Directory.GetFiles("c:\\tmp", "*.xml")
|> Array.iter (printfn "%s")
But I want only the file names so I tried:
System.IO.Directory.GetFiles("c:\\tmp", "*.xml")
|> Array.iter (System.IO.Path.GetFileName)
|> (printfn "%s")
This won't compile. It gives the error:
This expression was expected to have type
unit
but here has type
string
I searched for examples but couldn't find anything. I'm obviously missing something simple and fundamental, but what?
Since you are new, one thing to make it easer to fix errors is to think of statements like mathematical statements that can be built up of simpler functions but that can also be factored apart.
So by factoring apart your problem you can get a finer grained error that becomes easier to solve.
System.IO.Directory.GetFiles("c:\\tmp", "*.xml")
|> Array.iter (System.IO.Path.GetFileName)
|> (printfn "%s")
is factored apart into
let directoryArray = System.IO.Directory.GetFiles("c:\\tmp", "*.xml")
let nameArray = Array.iter (System.IO.Path.GetFileName) directoryArray
(printfn "%s") nameArray
now the errors should be much easier to understand
If we look at the signature of Array.iter
which is iter : ('T -> unit) -> 'T [] -> unit
we see that it needs a function ('T -> unit)
that takes a type and returns a unit
which means return nothing, in this case printing would work, however you do not want to return nothing you want to convert the array of directories into an array of filenames. So Array.iter
will not work and you need a Array function that applies a function to each item in the array and returns a new Array
, Array.map
does this map : ('T -> 'U) -> 'T [] -> 'U []
To better understand the Array functions and see how they work one can add a lambda function
.
Likewise with (printfn "%s")
; one can add a lambda function to pass in a value.
let directoryArray = System.IO.Directory.GetFiles("c:\\tmp", "*.xml")
let nameArray = Array.map (fun x -> (Path.GetFileName(x))) directoryArray
Array.iter (fun x -> printfn "%s" x) nameArray
Now we can simplify the statements using |>
System.IO.Directory.GetFiles("c:\\tmp", "*.xml")
|> Array.map (fun x -> (Path.GetFileName(x)))
|> Array.iter (fun x -> printfn "%s" x)
and simplify again by removing the lambdas
open System.IO
Directory.GetFiles("c:\\tmp", "*.xml")
|> Array.map Path.GetFileName
|> Array.iter (printfn "%s")