better-files icon indicating copy to clipboard operation
better-files copied to clipboard

Suggestion: Change listRecursively to recurse : Stream[File]

Open geggo98 opened this issue 10 years ago • 2 comments

When you change File.listRecursively to 'File.recurse' to operate on Stream[File], the code becomes more flexible. recurse would return Stream(this) for regular files, Stream(target) for symlinks and a Stream of the direct contents for a directory. It would be nice if the interface could also handle some state, so it can be limited on the depth and it can could handle circles (symlinks, hard links) by memorizing files it had already processed.

A Stream would be nice, because so files could be handled as soon as they are found. Interfacing with Akka streams or scalaz-streams should also be easy.

Unfortunately recursion is not so simple as just to flatMap, especially when handling some state (for depth or for already seen files). Hence I suggest two functions:

  • recurse(children : File => Stream[File]) : Stream[File] This function takes a function as a parameter that gives back the children of a file. The user can then provide a function that follows or ingores symlinks and that handles special files (like lnk files on Windows, Aliases on Mac OS X, Gnome's desktop entry files , etc.). Recurse follows all the files recursively.
  • recurseWithState[T, U <# File](initialState : T, children : (T, File) => (T, Stream[U])) or . Same as bove but additionally provides some state to each call of the child function. This function can use this state to track the depth or to identify already processed files. It can then decide to return an empty stream.

You could provide convenience functions for these cases. The latter one would be especially interesting, because it makes the handling of links easier. Perhaps even a combination of the two concepts (depth + already seen files).

geggo98 avatar Sep 16 '15 09:09 geggo98

I think that is exactly what java.nio.files.Files.walk does? I already have a TODO to "walk safely" i.e. do not throw IOExceptions if one of the nested files throw a read error.

pathikrit avatar Sep 18 '15 04:09 pathikrit

The nio function is similar, but more limited. It doesn't allow to introduce your own file-following functions (for Aliases, "lnk" files, etc.) and it doesn't allow to introduce your own functions to limit the recursion (cycle detection, shortest distance from a given starting point, limit to the same file system, etc.). The nio function just takes flags and parameters instead.

The API I proposed is more Scala like: It takes functions as parameters and it can be easily combined with standard Scala functionality. Very likely it could be built using a state monad, but this would bring in external dependencies (Cats or scalaz).

Btw. there is also a walkFileTree function in nio. This function would allow for custom recursion limitations using a visitor. But it still doesn't support custom file-following functionality.

geggo98 avatar Sep 18 '15 14:09 geggo98