【问题标题】:Swift fluent-like api design with custom operatorsSwift fluent-like api 设计与自定义操作符
【发布时间】:2017-01-29 12:32:21
【问题描述】:

我想用自定义运算符和函数编写某种类似流利的 api。这是我正在寻找的示例:

var result = [Section]()

result +++= Section()
    .appendTitle(title)
    .appendPhotos(photos)

result +++= Section()
    .appendSomething(sth)
    .appendPhotos(photos)
    .appendAnything()

return result

为此,我声明了两个自定义运算符:

infix operator +++{ associativity left precedence 95 }

func +++(lhs : [Section], rhs : Section) -> [Section] {
    var result = lhs
    result.append(rhs)
    return result
}

infix operator +++= { associativity left precedence 95 }

func +++=(inout lhs : [Section], rhs : Section) {
    lhs = lhs +++ rhs
}

当然还有 Section 结构的正确扩展:

extension Section {
    mutating func appendTitle(title: String?) -> Section {
        guard let unwrappedTitle = title
            else { return self }

        ...

        return self
    }

    mutating func appendPhotos(photos: [Photo]?) -> Section {
        ...
    }

    ...
}

不幸的是,它没有按预期工作......

单独的result +++= Section() 行是正确的,但是当我添加.append 时它不会编译。

第一条消息是:Passing value of type '[Section]' to an inout parameter requires explicit '&'

然后我尝试在result 前面加上一个&(但我从来没有为a += 1 这样做过)还有第二条消息:Cannot use mutating member on immutable value: function call returns immutable value

如果有人能提供帮助,将不胜感激。

我使用的是 Swift 2.2 和 Xcode 7。

【问题讨论】:

    标签: ios swift api operators fluent


    【解决方案1】:

    找到了!!!

    这些方法不应该发生变异:

    func appendTitle(title: String?) -> Section {
        guard let unwrappedTitle = title
            else { return self }
    
        var result = self
    
        ... // Work with result instead of self!!! 
    
        return result
    }
    

    【讨论】:

      【解决方案2】:

      嗯,您已经找到了解决方案。太好了!

      但是请允许我解释一下这里实际发生了什么。

      让我们仔细看看您之前的实现。

      extension Section {
          mutating func appendTitle(title: String?) -> Section {
      
              guard let unwrappedTitle = title else { return self }
              self.title = unwrappedTitle
              return self
          }
      
          mutating func appendPhotos(photos: [Photo]?) -> Section {
              guard let unwrappedPhotos = photos else { return self }
              self.photos = unwrappedPhotos
              return self
          }
      }
      

      我们知道(至少现在)运算符重载函数绝对没问题,没有错。

      给定扩展正在更改参数的值,因此它应该是变异的,而且确实如此。那为什么会报错:

      "不能对不可变值使用变异成员:函数调用返回 不可变的价值”

      原因:当调用“appendTitle”或“appendPhotos”时,它会返回一份self。不完全相同的参考!

      这是因为 swift 处理不变性的方式。如果针对值类型更改成员属性的值,则会创建一个副本,并将该值分配给相应的成员。

      因此,当您写信时:Section().appendTitle(title).appendPhotos(photos)

      Section() 返回 self 的副本,即 Section 的不可变版本。 因此,不能appendTitle 对其进行修改,或者一般情况下不能对其进行变异。

      这可以解决:

      var section = Section() section.appendTitle(title).appendPhotos(photos)

      但是现在appendTitle(title) 返回一个 self 的副本,即 Section 的不可变版本。并且同样的问题仍然存在。

      您所做的是,您没有使用 self 更改任何内容,而是创建了一个新实例,这次是使用 var 的可变实例。对其进行变异并返回。现在它不是自我变异,因此它不必是mutating func

      您提供的解决方案在这里运行良好,但实际上它是一个 hack,这里需要它来涵盖我现在所说的 Swift 中的“错误”。

      其他解决方案可以将struct Section 更改为class Section。在那里您不需要创建var result,只需更改属性并返回即可。

      这是我对 SO 的第一个回答 :)

      【讨论】:

      • 感谢您的回答,这是 swift 中的常见问题,由于多年的 OOP,我们通常(或至少部分)忘记通过值而不是通过引用传递参数有时会导致对语言。无论如何,谢谢,因为这是您的第一个答案,我会自豪地接受它,因为它比我的要明确得多。 ;)
      • @Zaphod,你的观点非常好。经过多年的 OOP,很容易忽视函数式语言的特性。 Swift 是一种函数式语言,它完全是关于不变性的。感谢接受。 :)
      猜你喜欢
      • 1970-01-01
      • 2019-04-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多