如何将多个monad绑定在一起



我在LanguageExt中使用TryAsyncmonad,但在尝试将多个monad绑定在一起时遇到了困难。我仍在学习函数式编程,所以这样做可能完全错误。请随意评论我的方法的任何部分。

假设我有以下方法调用Google Drive API。。。

TryAsync<File> GetFolder(string folderId)
TryAsync<string> CreateFolder(string folderName, string parentFolderId)
TryAsync<string> UploadFile(Stream file, string fileName, string mimeType, string folderId)

其中CCD_ 2是用于谷歌驱动器中的文件/文件夹的谷歌类型。

我可以单独调用其中的每一个,没有问题,并且可以使用Match来处理结果。

然而,我有时想调用多个,比如获取特定文件夹的File对象,然后创建一个新的子文件夹并上传一个文件。我知道我可以按如下方式完成(空气代码,所以请忽略任何拼写错误(。。。

(await GetFolder("123"))
.Match(async folder => {
(await CreateFolder("New folder", folder.Id))
.Match(async newFolder => {
(await UploadFile(stream, "New file name.txt", "text/text", newFolder.Id))
.Match(fileId => /* do whatever with the uploaded file's Id */, ex => /* report exception */);
}, ex => /* report exception */);
}, ex => /* report exception */);

正如你所看到的,这是非常痛苦的。我相信你应该能够将monad链接在一起,我认为使用Bind,所以你最终会得到更像这样的东西(再次,空气代码(。。。

(await GetFolder("123"))
.Bind(folder => CreateFolder("New folder", folder.Id))
.Bind(newFolder => UploadFile(stream, "New file name.txt", "text/text", newFolder.Id))
.Match(fileId => /* do whatever with the uploaded file's Id */, ex => /* report exception */);

然而,我无法获得任何代码进行这样的编译。

一个问题是,我不确定我的方法是否有正确的签名。它们应该返回Task<T>,并让调用代码使用TryAsync<T>,还是让方法本身返回TryAsync<T>

有人能建议我该怎么做吗?感谢

您的最后一次尝试是真的关闭,如果您检查了编译器错误,您可能已经发现了:

'string' does not contain a definition for 'Id' and no accessible extension method 'Id'
accepting a first argument of type 'string' could be found (are you missing a using directive
or an assembly reference?)

问题出在newFolder.Id上。原因是newFolder(正如错误所暗示的那样(是string,而不是File。这是因为上一个动作CreateFolder的结果返回的是TryAsync<string>,而不是TryAsync<File>

只需删除.Id属性:

var actual = await GetFolder("123")
.Bind(folder => CreateFolder("New folder", folder.Id))
.Bind(newFolder => UploadFile(stream, "New file name.txt", "text/text", newFolder))
.Match(fileId => handleFileId(fileId), ex => handleException(ex));

这会编译并完成任务。

如果您喜欢,也可以用查询语法编写表达式:

var actual = await (
from folder in GetFolder("123")
from newFolder in CreateFolder("New folder", folder.Id)
from fileId in UploadFile(stream, "New file name.txt", "text/text", newFolder)
select fileId)
.Match(fileId => handleFileId(fileId), ex => handleException(ex));

结果是一样的——C#编译器将使用Bind将查询语法转换为以前的方法绑定样式。因此,区别仅在于可读性。

有时查询语法更可读,有时方法调用语法更可读。在这种情况下,我认为前者是最可读的。

最新更新