繁体   English   中英

F# 功能回调

[英]F# functional Callback

我正在尝试在 F# 中迁移 C# 程序。 该程序是一个 FtpClient,它封装了 FluentFtp 库并通过如下接口公开 ftp 方法:

  public interface IFtpSession
{
    IEnumerable<string> ListFiles(string remoteFolder);
}

sealed class FtpSession : IFtpSession
{
    private FluentFTP.FtpClient FluentFtpClient { get; set; }

    internal FtpSession(FluentFTP.FtpClient fluentFtpClient)
    {
        this.FluentFtpClient = fluentFtpClient;
    }

    public IEnumerable<string> ListFiles(string remoteFolder)
    {
        return FluentFtpClient.GetListing(remoteFolder).Where(fileItem => fileItem.Type == FtpFileSystemObjectType.File).Select(fileItem => fileItem.FullName);
    }

}}

private static void CreateFtpSession(FtpConnectionSettings settings, Action<IFtpSession> onSessionOpen) {
        FluentFTP.FtpClient ftpClient = new FluentFTP.FtpClient(settings.Host, settings.Port, settings.UserName, settings.UserPassword);
        ftpClient.Connect();
        FtpSession ftpSession = new FtpSession(ftpClient);
        onSessionOpen(ftpSession);
        ftpClient.Disconnect();
    }

我想在 F# 中做同样的事情,但要以实用的方式。 这意味着我不想使用具有成员的对象以 OO F# 样式复制上述代码,但只使用函数和值类型。

做这种工作的正确方法是什么?

在上面的示例中,只有一个 function (ListFiles),但我的问题假设接口 IFtpSession 可以具有其他功能并且不遵守单一责任原则。

非常感谢你的帮助

functional programming中非常重要的两件事是:

  • 分离datafunctions (就像您在问题中提到的那样)。
  • 分离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被两个函数listFilesdownloadFiles重用,与您的回调设置具有相同的结果。

在这里您还可以看到分离的纯代码和不纯代码; 不纯的代码是使用ftp客户端的代码,纯代码是过滤和映射来自ftp的结果(如果您将过滤和映射代码提取到单独的 ZC1C425268E68385D1AB4ZZ 中,但您的示例可以忽略该unit test ) .

希望这能澄清一些事情。

使用计算表达式能够在不同的 FTP 工作流中重用可能是您正在寻找的最强大的解决方案。 您甚至可以使用内置的异步表达式。 它可能是这样的:

然后像这样使用它

async {
   using ftp = FluentFTP...
   // do your things here
}
|> Async.RunSynchronously

或者,您可以构建自己的计算表达式来处理所有构建和拆卸,但这是 F# 中更强大的构造之一,往往被忽略。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM