【问题标题】:How do I create a new type-safe primitive type, based on Int?如何基于 Int 创建新的类型安全原始类型?
【发布时间】:2020-02-27 01:03:59
【问题描述】:

问题背景:我正在同时学习函数域建模和 Swift。我正在使用的示例在 F# 中。一个这样的例子是声明单例联合:

//F# code
type CustomerId = CustomerId of int
type OrderId = OrderId of int

let customerId = CustomerId 42
let orderId = OrderId 42

printfn "%b" (orderId = customerId) //produces a compile error

因此,在 F# 中,声明本质上都是 Ints 的类型安全原语似乎非常容易。 在 Swift 中 typealias 不会做同样的事情:

typealias CustomerId = Int
typealias OrderId = Int

let customerId: CustomerId = 42
let orderId: OrderId = 42

customerId == orderId // true!

现在,我知道 Swift 中的所有原始类型本质上都是 structs,但我还远远不够复杂,无法理解 Swift 代码库来弄清楚 Int 是如何创建的。 有人知道与 F# 声明等效的简单类型安全 Swift 吗?

【问题讨论】:

标签: swift types functional-programming


【解决方案1】:

typealias 实际上只是一个别名。它不创建类型。它只是让输入别名的东西更容易。

做你想做的事情的方法是:

struct CustomerID {
    let value: Int
}

struct OrderID {
    let value: Int
}

如果您想要一个“裸”初始化程序(CustomerID(4) 而不是 CustomerID(value: 4)),您可以添加:

struct CustomerID {
    let value: Int
    init(_ value: Int) { self.value = value }
}

由于您可能会经常这样做,因此您可以将其提取到协议中

protocol Identifier {
    typealias IDType
    init(value: IDType)
}

extension Identifier {
    init(_ value: Int) { self.init(value: value) }
}

extension CustomerID: Identifier {}

当然,如果您想要像 = 42 这样的语法,您可以使用 ExpressibleByIntegerLiteral 添加类似的扩展。

您在 F# 中讨论的特定语法在 Swift 世界中通常通过其 Haskell 名称 newtype 来引用。 Swift 没有 newtype 和 it's been discussed several times 这样易于使用的功能。在 Swift 中有几个极端情况,决定如何自动符合协议(特别是需要 Self 的协议)有点复杂。但它可能有一天会实施。

您可以在2019 AltConf talk on the subject 看到更深入的讨论。

【讨论】:

  • 谢谢 Rob,这是一个很好的答案! YouTube 视频很棒,虽然有点超出我的想象 - 20 分钟的标记确实正面解决了这个问题。这让我认真思考所有后果和影响,这很好。
猜你喜欢
  • 1970-01-01
  • 2016-10-15
  • 1970-01-01
  • 1970-01-01
  • 2021-01-04
  • 1970-01-01
  • 2020-10-16
  • 2018-01-07
  • 1970-01-01
相关资源
最近更新 更多