首页 > 解决方案 > 返回联合类型而不是联合大小写

问题描述

我有这样的联合类型

  type AccountCreated =
    { Owner: string
      AccountId: Guid
      CreatedAt: DateTimeOffset
      StartingBalance: decimal }

  type AccountDebited =
    { To: Guid
      From: Guid
      Description: string
      Time: DateTimeOffset
      Amount: decimal }

  type AccountCredited =
    { To: Guid
      From: Guid
      Description: string
      Time: DateTimeOffset
      Amount: decimal }

  type AccountEvent =
    | Created of AccountCreated
    | AccountCredited of AccountCredited
    | AccountDebited of AccountDebited

还有一个像这样的联合类型:

 type RegisteredAccount = {
      Owner: string
      Balance: decimal
      AccountId: Guid }
  
  type Account =
    | Unregistered
    | Registered of RegisteredAccount

有一个功能evolve

  let evolve state event: Account =
    match event with
      | Created accountCreated ->
          { AccountId = accountCreated.AccountId
            Owner = accountCreated.Owner
            Balance = accountCreated.StartingBalance }
      | AccountDebited accountDebited ->
          match state with
          | Registered s -> 
              { s with
                  Balance = s.Balance - accountDebited.Amount }
          | _ -> failwith "unregistered account can't be debited"
      | _ -> failwith "todo: other cases"

evolve应该使用List.fold

let build = List.fold evolve
let rebuild = build Unregistered

如果我没有明确指定evolveas的返回类型Account,则会收到以下错误let build = List.fold evolve

Type mismatch. Expecting a 'Account -> AccountEvent -> Account' but given a 'Account -> AccountEvent -> RegisteredAccount' The type 'Account' does not match the type 'RegisteredAccount'

如果将返回类型设置为evolve,我会在模式匹配中得到编译器错误,而在模式匹配中出现类似Created的错误。RegisteredAccountDebited

This expression was expected to have type 'Account' but here has type 'RegisteredAccount'

我该如何解决这个问题?

标签: f#

解决方案


我认为您的代码中的问题是该evolve函数试图返回RegisteredAccount记录类型的值而不是Account联合类型的值。

我看到您想使用Unregisteredvalue (of type Account) 作为初始值,并且您还添加了一个类型注释,指定的返回类型evolve应该是Account。我认为这实际上是你想要的。唯一缺少的是您需要使用Registeredunion case 将返回的值包装RegisteredAccountAccount.

以下类型检查对我来说很好:

let evolve state event: Account =
  match event with
    | Created accountCreated ->
        { AccountId = accountCreated.AccountId
          Owner = accountCreated.Owner
          Balance = accountCreated.StartingBalance } |> Registered
    | AccountDebited accountDebited ->
        match state with
        | Registered s -> 
            { s with
                Balance = s.Balance - accountDebited.Amount } |> Registered
        | _ -> failwith "unregistered account can't be debited"
    | _ -> failwith "todo: other cases"

我所要做的就是|> Registered在您返回记录的两个地方添加!


推荐阅读