Две вещи, которые очень важны для functional programming
:
- Разделение
data
и functions
(как вы упомянули в своем вопросе). - Разделение
pure
и impure
(все с побочными эффектами ) кода. Нечистый код непредсказуем и труден для тестирования, поэтому мы хотим получить как можно больше чистого кода и перенести нечистый код в границы приложения.
Адаптируя свой код в F#
, вы можете получить что-то вроде этого
open FluentFTP
let listFiles (client:FtpClient, remoteFolder:string) =
let items = client.GetListing(remoteFolder)
items
let downloadFiles (client:FtpClient, localDir:string, remotePaths:seq<string>) =
let _ = client.DownloadFiles(localDir, remotePaths)
() // return unit, similar to 'void' in C#
[<EntryPoint>]
let main _ =
use client = new FtpClient() // pass arguments in ctor
client.Connect();
let items = listFiles (client, "dir") // impure
let fileNames = items |> Seq.filter (fun x -> x.Type = FtpFileSystemObjectType.File) // pure
|> Seq.map (fun x -> x.FullName)
let localDir = "C:\Temp"
downloadFiles (client, localDir, fileNames) // impure
0 // return an integer exit code (client also gets disposed)
Это консольное приложение и компилирует (я не проверял его). main
создает IDisposable
FtpClient
, это устраняется с помощью ключевого слова use
. Вы можете думать об этом main
как о dependency injection
бите приложения, оно создает и удаляет все зависимости всех вещей. client
повторно используется обеими функциями listFiles
и downloadFiles
, что дает тот же результат, что и ваша функция обратного вызова .
Здесь вы также видите чистый и нечистый код, разделенные;нечистым является код с использованием клиента ftp
, а чистый код фильтрует и отображает результаты из ftp
(если вы извлекаете код фильтрации и сопоставления в отдельную функцию, вы могли бы unit test
this, но я оставил этов этом примере).
Надеюсь, это немного прояснит ситуацию.