[英]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
中非常重要的两件事是:
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
的结果(如果您将过滤和映射代码提取到单独的 ZC1C425268E68385D1AB4ZZ 中,但您的示例可以忽略该unit test
) .
希望这能澄清一些事情。
使用计算表达式能够在不同的 FTP 工作流中重用可能是您正在寻找的最强大的解决方案。 您甚至可以使用内置的异步表达式。 它可能是这样的:
然后像这样使用它
async {
using ftp = FluentFTP...
// do your things here
}
|> Async.RunSynchronously
或者,您可以构建自己的计算表达式来处理所有构建和拆卸,但这是 F# 中更强大的构造之一,往往被忽略。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.