Swift Number Generics

Swift number generics?

Swift generics aren't like C++ templates.

In C++, you can just try to use a parameterized type however you want, and it's not an error until the compiler tries to instantiate the template with some type that doesn't support what your template tries to do.

In Swift, the generic construct can only use a parameterized type in ways known to be valid when the generic construct is first parsed. You specify these "ways known to be valid" by constraining the parameterized type with protocols.

You cannot call sqrt or pow with generic-typed arguments, because those functions are not themselves generic. They have each two definitions:

func pow(_: Double, _: Double) -> Double
func pow(lhs: Float, rhs: Float) -> Float
func sqrt(x: Double) -> Double
func sqrt(x: Float) -> Float

You could write type-specific versions of hypotenusa:

func hypotenusa(a: Float, b: Float) -> Float
func hypotenusa(a: Double, b: Double) -> Double
func hypotenusa(a: CGFloat, b: CGFloat) -> CGFloat

I'm not sure why you'd create an Int version at all, since very few right triangles have integer hypotenuses.

Anyway, you don't need to define the Float and Double versions at all, because the standard library already provides a hypot function defined on Float and Double:

func hypot(_: Double, _: Double) -> Double
func hypot(lhs: Float, rhs: Float) -> Float

You could create another override for CGFloat:

func hypot(l: CGFloat, r: CGFloat) -> CGFloat {
return hypot(Double(l), Double(r))
}

As for your addition function, it has the same problem as your hypotenusa function: the + operator is not defined entirely generically. It has some generic definitions (unlike sqrt and pow), but those only cover the integer types (see IntegerArithmeticType). There's not generic definition of + that covers the floating-point types. Swift defines all of these versions of + with explicit types:

func +(lhs: Float, rhs: Float) -> Float
func +<T>(lhs: Int, rhs: UnsafePointer<T>) -> UnsafePointer<T>
func +<T>(lhs: UnsafePointer<T>, rhs: Int) -> UnsafePointer<T>
func +(lhs: Int, rhs: Int) -> Int
func +(lhs: UInt, rhs: UInt) -> UInt
func +(lhs: Int64, rhs: Int64) -> Int64
func +(lhs: UInt64, rhs: UInt64) -> UInt64
func +<T>(lhs: Int, rhs: UnsafeMutablePointer<T>) -> UnsafeMutablePointer<T>
func +<T>(lhs: UnsafeMutablePointer<T>, rhs: Int) -> UnsafeMutablePointer<T>
func +(lhs: Int32, rhs: Int32) -> Int32
func +(lhs: UInt32, rhs: UInt32) -> UInt32
func +(lhs: Int16, rhs: Int16) -> Int16
func +(lhs: UInt16, rhs: UInt16) -> UInt16
func +(lhs: Int8, rhs: Int8) -> Int8
func +(lhs: UInt8, rhs: UInt8) -> UInt8
func +(lhs: Double, rhs: Double) -> Double
func +(lhs: String, rhs: String) -> String
func +(lhs: Float80, rhs: Float80) -> Float80

Swift generic number types and math

As of Swift 3, all floating point types conform to FloatingPoint,
and all integer types conform to Integer.
Both protocols define the basic arithmetic operations like +,-,*,/.
Also the floor() function is defined for FloatingPoint
arguments.

Therefore in your case I would define two implementations, one for
integers and one for floating point values:

func mod<N: Integer>(_ x: N, _ y: N) -> N {
return x - y * (x/y) // or just: return x % y
}

func mod<N: FloatingPoint>(_ x: N, _ y: N) -> N {
return x - y * floor(x/y)
}

FloatingPoint has also a truncatingRemainder method,
a.truncatingRemainder(b) is the "floating point equivalent"
to a % b for integers. It gives the
gives the same result as your mod function if both
operands have the same sign.

Swift generic class with numeric and comparable protocols

Since your class is a "RangeValidator", I suggest making your class initializer take a ClosedRange<T>. Declaring T: Comparable allows you to use range.contains(value) to do the validation:

Note: There is no need to restrict your class to Numeric, but if you want to then you can declare it as class NumberRangeValidator<T: Comparable & Numeric> as @JoakinDanielson mentioned in the comments.

class NumberRangeValidator<T: Comparable & Numeric> {

let range: ClosedRange<T>

init(range: ClosedRange<T>) {
self.range = range
}

// allow range to be specified with minValue, maxValue
init(minValue: T, maxValue: T) {
guard minValue <= maxValue else { fatalError("Can't form Range with maxValue < minValue") }
self.range = minValue...maxValue
}

func validate(_ value: T) -> Bool {
return range.contains(value)
}
}

Examples:

let validator = NumberRangeValidator(range: 10.0 ... 30.0)

print(validator.validate(9)) // false
print(validator.validate(10)) // true
print(validator.validate(20)) // true
print(validator.validate(30)) // true
print(validator.validate(31)) // false

let floatValidator = NumberRangeValidator(minValue: Float(0), maxValue: 5)
print(floatValidator.validate(0)) // true
print(floatValidator.validate(10)) // false

Note: There is no reason this needs to be a class. A struct will do the job just as well.

How to implement generic function for adding both numbers and String in Swift?

One possible solution is this. You know you want your method to be applicable to anything that has a + defined... So define an explicit protocol. Let's call it Addable:

protocol Addable {
static func +(lhs: Self, rhs: Self) -> Self
}

Now, using extensions, declare conformance to Addable for the types you care about:

extension String: Addable {}
extension Int: Addable {}
extension Double: Addable {}

And define your add function as:

func  add<T: Addable>(first: T, second: T) -> T {
return first + second
}

Is it possible to divide two numerics in swift?

If you check the documentation of Numeric, it clearly shows its values only need to support multiplication.

The division operator (/) is defined on the BinaryInteger and FloatingPoint protocols separately, since they have different semantics, so you cannot divide any numeric types by each other.

Using Any instead of generics

In swift, you can assign value to a variable ONLY if the type of the variable is same with the value. So imagine you have this:

var a = 1
var b = 2

func swapTwoValues(_ a: inout Any, _ b: inout Any) {
let temporaryA = a
a = b
b = temporaryA
}

swapTwoValues(&a, &b) // <- Cannot pass immutable value as inout argument: implicit conversion from 'Int' to 'Any' requires a temporary

So compiler forces you to implicitly assign Any for the type of the variables. This is not what you want I think. So you have to tell the compiler that it doesn't matter the type of the arguments. The only thing matters is they both have same type to fulfill compilers need. So to achieve this, you can use generic or some kind of protocol.

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}

swapTwoValues(&a, &b) // Done

Tip: You can swap values with tuple in single line of code without using temporaryVar like this: (Even without function and generic and etc.)

(a, b) = (b, a)

Why I can not access member of generic in swift

It's because T.ItemType is ambiguous.

At the point that the compiler is looking at your expression all it really knows is that T.ItemType is an associatedType. It doesn't really know what properties a particular instance assigned to ItemType might have.

Consider this code:

struct Marshmallow {
}

struct SmoresStruct : ProtoB {
typealias ItemType = Marshmallow

var id: UUID = UUID()
var arrayOfItems: [Self.ItemType] = Array<Marshmallow>()
}

class SmoresClass : M<SmoresStruct> {
}

SmoresStruct is a struct that satisfies the constraint that it implements ProtoB, and it can be used to create SmoresClass (a subclass of class M) because it satisfies all the constraints you've placed on the generic parameter of class M. But the ItemType, Marshmallow, is not Identifiable so at the point in the implementation of class M where you try to imply that T.ItemType should have an id property, this is one instance where it does not.

You need an additional constraint on your declaration of the M Class:

class M<T : ProtoB> where T.ItemType : Identifiable {
...
}

Now if you try to use Marshmallow as an ItemType you will get:

Type 'SmoresStruct.ItemType' (aka 'Marshmallow') does not conform to protocol 'Identifiable'

How to calculate percentage with generics in Swift?

...If Generic is really the way you want to go with this:

You'll want to start by defining some constraints on T to constrain it to a type that make sense to take a percentage of. From there, you'll simply need to iterate the possibilities and cast, or define your own operator on a protocol you're conforming to.

You can read about custom operators here: https://docs.swift.org/swift-book/LanguageGuide/AdvancedOperators.html

In theory you could do something like this:

protocol Percentable {
var value: Double {get, set}
}

extension Percentable {
static prefix func % () -> Percentable {
// DO percentage logic here
return value
}
}

And then you could call this like the following:

func unitX<T>(x: T) -> T where T: Percentable {
return x %
}

Note: You would have to then make an extension on Int or Double to be Percentable.

Recap

This would be a horrible approach since you would simply be converting from and Int to a Double back to an Int. It would make more sense (like mentioned in the comments) to simply overload Ints to divide by Doubles and visa-versa to keep your precision. Generics are not a good use here.

Swift generics: How to represent 'no type'?

Types are sets of values, like Int is the set of all integer numbers, String is the set of all sequences of characters and so on.

If you consider the number of items in the set, there are some special types with 0 items and 1 items exactly, and are useful in special cases like this.

Never is the type with no values in it. No instance of a Never type can be constructed because there are no values that it can be (just like an enum with no cases). That is useful to mark situations, code flow etc as 'can't happen', for example the compiler can know that a function that returns Never, can never return. Or that a function that takes Never can never be called. A function that returns Result<Int, Never> will never fail, but is in the world of functions returning Result types. But because never can't be constructed it isn't what you want here. It would mean a Promise that can't be fulfilled.

Void is the type with exactly 1 value. It's normally spelled () on the left and Void on the right, of a function definition. The value of a void is not interesting. It's useful to make a function that returns Void because those are like what other languages call subroutines or procedures. In this case it means a Promise that can be fulfilled but not with any value. It can only be useful for its side effects, therefore.



Related Topics



Leave a reply



Submit