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
Most Efficient/Realtime Way to Get Pixel Values from iOS Camera Feed in Swift
How to Detect If a Nsattributedstring Contains a Nstextattachment and Remove It
Setting a Stateobject Value from Child View Causes Navigationview to Pop All Views
Cmpedometer Querypedometerdatafromdate Returns Error 103
Converting an Existing Project into Customizable Framework
Icloud Document Picker from Wkwebview Dismissing Container View
Swift: Bring View from Stack View to Front
How to Set Up Gccontroller Valuechangehandler Properly in Xcode
Changing Font Size in a Label for Only iPhone 4S, Is This Possible
Split String into Array of Arrays
How to Add a Ibaction to a Button Programmatically in Swift 4
Swift Continuous Rotation Animation Not So Continuous
How to Run My Performance Tests More Than Ten Times
How to Convert This Opengl Pointer Math to Swift