Is it possible to create a “Positive Number” type in Swift?

白昼怎懂夜的黑 提交于 2020-01-07 03:06:40

问题


Sorry if this is a stupid question but I'm wondering if there's a way in Swift to create a type that exclusively holds numbers that are strictly greater than zero and where the "positiveness" of the values is enforced at compile time.

For example, can I create somehow write code like

func divide(x: PositiveNumber, y: PositiveNumber){
    return x / y
}

such that

divide(1, 3)

works but

divide(1, 0)

won't compile?

The closest thing I could come up with was a struct with only one fallible initializer such that the type has either a positive value or is nil:

struct PositiveNumber {
    let value: Float

    init?(value: Float){
        if value > 0 {
            self.value = value
        } else {
            return nil
        }
    }
}

func / (left: PositiveNumber, right: PositiveNumber) -> Float {
    return left.value / right.value
}

func divide(x: PositiveNumber?, y: PositiveNumber?) -> Float? {
    if let x = x, y = y {
        return x / y
    }
    return nil
}

let x1 = PositiveNumber(value: 1)
let y1 = PositiveNumber(value: 3)

let x2 = PositiveNumber(value: -1)
let y2 = PositiveNumber(value: 0)

divide(x1, y: y1)!  // .333
divide(x2, y: y2)!  // runtime error

That's not terrible but we still have to deal with a lot of optional handling/unwrapping. I'm asking this question because I have many places in my code where I need to check that a value is not zero and I'm curious if there's a way to remove that code and let the compiler handle it. The struct-based solution requires pretty much the same amount of code.


回答1:


Number Type build on Float

This gist is contains a Struct that conforms to pretty much everything Float conforms too. It is just a vanilla copy of Float, change it to your liking.


Have you considered a custom operator?

infix operator /+ { associativity left precedence 150 }

func /+(lhs:Float,rhs:Float) -> Float? {

    guard rhs > 0 else {
        return nil
    }
    return lhs / rhs
}

let test = 2 /+ -1 // nil
let test2 = 2 /+ 1 // 2

let test3 = 2 /+ 1 + 2 // warning

It doesn't really matter if you let it return an optional, or an enum value, or different protocols. You will have to handle the return. But this way you get compiler warnings.


Limited Number Type with just an operator to handle divisions:

You could change math altogether and create a PositiveNumber Type that returns NaN when dividing by a value less than Zero.

public struct PositiveFloat {
    public var value: Float
    /// Create an instance initialized to zero.
    public init() {
        self.value = 0
    }
    /// Create an instance initialized to `value`.
    public init(_ value: Float) {
        self.value = value
    }

    public init(_ value: PositiveFloat) {
        self.value = value.value
    }
}

extension Float {
    public var positive : PositiveFloat {
        return PositiveFloat(self)
    }
}


public func /(lhs:Float,rhs:PositiveFloat) -> Float {
    if 0 > rhs.value {
        return lhs / rhs.value
    } else {
        return Float.NaN
    }
}
public func /(lhs:PositiveFloat,rhs:PositiveFloat) -> Float {
    if 0 > rhs.value {
        return lhs.value / rhs.value
    } else {
        return Float.NaN
    }
}

let testNormal : Float = 10
let testFloat : Float = -5

let test = testFloat / testNormal.positive
if test.isNaN {
    // do stuff
}


来源:https://stackoverflow.com/questions/34260199/is-it-possible-to-create-a-positive-number-type-in-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!