返回匿名记录而不是元组的注意事项

Caveats of returning anonymous records instead of tuples

考虑以下(一般,可以在库中公开获得)实用程序函数,用于将字符串解析为 HTTP 基本凭据并 return 输入用户名和密码:

let parseBasicCredentials (encodedCredentials: string) =
  (* approx. 10 lines of pipes, matching and try/with *)
  username, password

由于用户名和密码都是字符串,调用者需要记住(或查看文档)用户名 returned 作为元组的第一个元素,密码作为第二个元素。

但是,F# 将很快获得对匿名记录的支持(已在 nightlies 中提供)。发生这种情况时,此类函数还可以 return 匿名记录:

let parseBasicCredentials (encodedCredentials: string) =
  (* approx. 10 lines of pipes, matching and try/with *)
  {| Username = username; Password = password |}

这似乎工作正常,但由于这是一个全新的语言功能,我不确定它是否有任何缺点,无论是技术上的还是只是不受欢迎。 AFAIK returning 一个元组是一种惯用且被接受的解决方案,适用于像这样的简单辅助函数。匿名类型还允许您命名元素,因此可以考虑更多 "safe",因为它更自记录,因此更好地防止调用者混淆 returned 值。

我不考虑通用(非域)辅助函数,例如 returning 单独定义的记录类型或单例 DU 元组的候选项。我只是好奇,既然存在匿名记录,当需要像这里这样的临时、非原始 return 值时,它们是否可以用作 "better tuples",或者元组是否仍然是首选(并且,如果是,原因是什么)。

我没有经常使用匿名记录,但我认为主要的限制是您不能编写将它们作为输入的函数。这严重限制了它们作为在函数之间传递的应用程序范围数据的使用,这是一种有意的设计选择。

作为在函数或脚本文件中存储瞬态数据的便捷方式,它们将更有用。

更正:

可以将它们作为输入。这是一个例子:

let showCredentials (x:{| Username:string; Password:string |}) =
    printfn "Username: %s, Password %s" x.Username x.Password

但是,您必须提及记录中的所有字段才能匹配类型。因此,要编写另一个接受相同类型但不使用密码的函数,您可以这样写:

let showUsername (x:{| Username:string; Password:_ |}) =
    printfn "Username: %s" x.Username

这意味着每次您编辑类型定义时,您都必须编辑该类型的函数参数的所有类型注释。对于一般用途,这不能很好地扩展。

可能会添加模式匹配,因此您可以改写:

let showUsername {| Username = username |} =
    printfn "Username: %s" username

但我不清楚这是否会添加到语言中。