首页 > 解决方案 > 用于返回具有不同结构的匿名记录的有效 F# 函数

问题描述

我正在编写一个 F# 库来调用 HTTP REST API。HTTP 请求的 JSON 正文根据请求应发回的信息量或信息类型而有所不同。

例如,下面是使用匿名记录创建 json 主体的代码,用于 HTTP 请求以列出项目中的文件,https://documentation.dnanexus.com/developer/api/search#api-method-system-查找数据对象

type Request = {
    ApiToken: ApiToken
    ProjectId: ProjectId
    StartingAt: ObjectId option
}

module Request =
    let toJson request =
        let (ProjectId projectId) = request.ProjectId
        let baseJson = {| scope = {| project = projectId
                                     recurse = true |}
                          describe = true |}

        match request.StartingAt with
            | Some (ObjectId objectId) ->
                {| baseJson with starting = {| project = projectId
                                               id = objectId |} |}

            | None ->
                baseJson

此代码无法编译。相反,在此示例的最后一行中,编译器会引发以下错误:

This anonymous record does not have enough fields. Add the missing fields [starting].F# Compiler(1)

是否可以编写一个返回具有不同结构的匿名记录的函数?

标签: f#

解决方案


这不是匿名记录本身的问题 - 没有明确的返回类型toJson- 根据分支,您返回不同的类型。

你有几个选择:

  1. starting创建一个对象,可以在 None 分支上将其设置为 null :
        match request.StartingAt with
            | Some (ObjectId objectId) ->
                {| baseJson with starting = box {| project = projectId
                                                   id = objectId |} |}

            | None ->
                {| baseJson with starting = null |}
  1. 在此函数中执行 json 序列化:
        match request.StartingAt with
            | Some (ObjectId objectId) ->
                serialize
                    {| baseJson with starting = {| project = projectId
                                                   id = objectId |} |}

            | None ->
                serialize baseJson

其中 serialize 是一个函数,它接受任何 'T 并返回一个字符串。

  1. 最后一种选择是用一个有区别的联合来统一这两个分支。但是,在这种情况下,我认为这不值得,因为无论如何您都在序列化此记录。

主要的是 match 表达式的两个“分支”都必须返回相同的类型。这对于 F# 中的任何分支表达式都是相同的,例如 match 或 if / else。它适用于所有类型——原语、记录、元组、联合等。


推荐阅读