M.Y. Babt
M.Y. Babt

Reputation: 2891

Error FS0001: The type 'string -> seq<unit>' is not compatible with the type 'seq<'a>'

I have a difficult time understanding the error messages in F#, as this is my first time using a typed functional language. Here is an example of my code:

let getAllFiles (directory : string) =
    Directory.GetFiles(directory)
let getFileInfo =
    getAllFiles 
    |> Seq.map (fun eachFile -> (eachFile, new FileInfo(eachFile)))

I received the following error message: "Error FS0001: The type 'string -> seq' is not compatible with the type 'seq<'a>'"

What changes can I make? Thank you in advance.

Upvotes: 3

Views: 1856

Answers (2)

Nikon the Third
Nikon the Third

Reputation: 2831

Since you are composing functions, not passing a value from one function to another, you have to use the composition operator (>>) instead of the pipe (|>):

open System.IO

let getAllFiles (directory : string) =
    Directory.GetFiles(directory)

let getFileInfo =
    getAllFiles 
    >> Seq.map (fun eachFile -> (eachFile, new FileInfo(eachFile)))

Alternatively, you can make the argument explicit and then you can use the pipe:

open System.IO

let getAllFiles (directory : string) =
    Directory.GetFiles(directory)

let getFileInfo directory =
    directory
    |> getAllFiles
    |> Seq.map (fun eachFile -> (eachFile, new FileInfo(eachFile)))

Update for the comment:

If I specifically want to get the file length, how should I write it instead?

open System.IO

let getAllFiles directory =
    Directory.GetFiles directory 

let getFileInfos =
    getAllFiles
    >> Seq.map (fun fileName -> (fileName, FileInfo fileName))


let getFileLengths =
    getFileInfos
    >> Seq.map (fun (fileName, fileInfo) -> fileName, fileInfo.Length)

If you mean to get all file lengths of all the files in the directory, you could use a getFileLengths function like in the code above. Note that I modified the name of getFileInfo.


Update for the second comment:

I would like to group the tuples according to the length of each file using Seq.groupBy. How may I go about doing so?

The function removeSingletonGroups removes all groups that contain only one value.

The function getFilesGroupedByLength first uses Seq.groupBy to create groups with the file lengths as keys, then uses Seq.map to remove the file length from the values, so the value of each group is only a list of filenames. Finally it uses removeSingletonGroups to purge all groups that contain only one file.

let removeSingletonGroups groupedSeq =
    groupedSeq
    |> Seq.filter (snd >> List.length >> (<>) 1)

let getFilesGroupedByLength =
    getFileLengths
    >> Seq.groupBy snd
    >> Seq.map (fun (fileLength, files) -> fileLength, files |> Seq.map fst |> List.ofSeq)
    >> removeSingletonGroups

Final(?) update:

I just saw your question regarding the duplicate file checker. I think this approach is what you were looking for:

open System.IO

let getFilesOfDirectory directoryPath = 
    Directory.EnumerateFiles directoryPath

let getFileLength filePath =
    (FileInfo filePath).Length

let groupFilesByLength files =
    files
    |> Seq.groupBy getFileLength

let removeSingletonGroups groupedSeq =
    groupedSeq
    |> Seq.filter (fun (_, group) -> Seq.length group > 1)

let getDuplicateFileCandidates directoryPath =
    directoryPath
    |> getFilesOfDirectory
    |> groupFilesByLength
    |> removeSingletonGroups

Upvotes: 3

Lee
Lee

Reputation: 144136

You need to provide the directory to getAllFiles e.g.

let getFileInfo =
    getAllFiles "directory to search"
    |> Seq.map (fun eachFile -> (eachFile, new FileInfo(eachFile)))

usually, when you get an error like you have, where a function type a -> ... -> b is not compatible with type b then you have not provided enough arguments to the function you're using.

Upvotes: 0

Related Questions