I have a sample example:
struct Test<T: SignedNumeric & Comparable> {
let n: T
init(n: T) {
self.n = n
}
}
extension Test where T: BinaryInteger {
func half() -> T {
return self.n / 2
}
func printHalf() {
print(self.half())
}
}
extension Test where T: FloatingPoint {
func half() -> T {
return self.n / 2
}
func printHalf() {
print(self.half())
}
}
Test(n: 2).printHalf() // print "1"
Test(n: 2.2).printHalf() // print "1.1
This code compiles and works. But I do not like:
1) I must additionally declare 2 extensions.
2) I have to duplicate printHalf method.
How correctly to implement this example? (just is inside the method?)
Related
First, I'm new to Swift language and have worked using python which is 'dynamic typed' language, so don't know exact compiler's perspective.
Below code is a simple code snippet to practice what Apple said POP(protocol oriented programming).
Snippet contains various ReturnClasses, RunnerClasses(each has different return type) and actual runner which just run RunnerClasses. Also has Selector enum to pretend dynamic dispatch.
protocol Return {
var value: Int { get set }
init(_ value: Int)
}
final class Return0: Return {
var value: Int
init(_ value: Int) {
print("return 0")
self.value = value
}
}
final class Return1: Return {
var value: Int
init(_ value: Int) {
print("return 1")
self.value = value + 50
}
}
final class Return2: Return {
var value: Int
init(_ value: Int) {
print("return 2")
self.value = value + 500
}
}
protocol FirstProtocol {
init()
func a() -> Return
func b() -> Return
}
extension FirstProtocol {
func a() -> Return {
print("protocol a hello")
return self.b()
}
func b() -> Return {
print("protocol b hello")
return Return0(5)
}
}
enum Selector {
case first, second
func cls() -> FirstProtocol{
switch self {
case .first:
return First1Class()
case .second:
return First2Class()
}
}
}
final class First1Class: FirstProtocol {
init() {
}
func b() -> Return1 {
print("first 1 class hello")
return Return1(3)
}
}
final class First2Class: FirstProtocol {
init() {
}
func b() -> Return2 {
print("first 2 class hello")
return Return2(5)
}
}
final class Runner {
var cls: FirstProtocol
var selector: Selector
init(_ selector: Selector) {
self.cls = selector.cls()
self.selector = selector
}
func createCls(cls: FirstProtocol.Type) -> FirstProtocol {
return cls.init()
}
func run(cls: FirstProtocol.Type) {
print("--- run 1 ---")
(self.createCls(cls: cls) as! First2Class).a()
}
func run2(t: Selector) -> Return {
print("--- run 2 ---")
print(t.cls())
return t.cls().b()
}
func run3() -> Return {
print("--- run 3 ---")
print(self.cls)
return self.cls.a()
}
}
final class Runner2 {
var runner: Runner
init(runner: Runner) {
self.runner = runner
}
func run() -> Return {
print("--- 2run 1 ---")
return self.runner.run2(t: .second)
}
}
var a = Runner2(runner: Runner(.second)).run()
print(type(of: a))
print(a.value)
And returns below result.
--- 2run 1 --- line 1
--- run 2 --- line 2
__lldb_expr_80.First2Class line 3
protocol b hello line 4
return 0 line 5
Return0 line 6
5 line 7
And here I have a question.
In line 3 which is result of Runner.run2, I think __lldb_expr_80.First2Class string means compiler exactly knows which class should be initiated. Therefore line 4 should be first 2 class hello not protocol b hello. Why protocol b hello is printed and how to make first 2 class hello is printed?
This is because the b in First2Class is not actually a witness for the b requirement in the FirstProtocol (same goes for First1Class). In other words, the reason why First2Class conforms to FirstProtocol is not the b method declared in it. The only witness for the protocol requirement is only the b in the protocol extension.
This is because the return types of these bs don't match the protocol requirement's return type:
// protocol requirement
func b() -> Return {
...
}
// First1Class
func b() -> Return1 {
...
}
// First2Class
func b() -> Return2 {
...
}
Return types must match exactly in order for a type to implement a protocol. See more discussions here.
When you call b on a protocol type, such as here:
return t.cls().b()
(t.cls() is of compile-time type FirstProtocol) because the bs are not the witness, your bs do not get considered when resolving the call. The call will only resolve to the only witness, which is the b in the protocol extension.
...means compiler exactly knows which class should be initiated.
That's not true. Remember that you only see the message printed at runtime. The compiler doesn't know which class is instantiated by cls.
Here is a shorter example that demonstrates this:
class A {}
class B : A {}
protocol Foo {
func foo() -> A
}
extension Foo {
func foo() -> A {
print("Foo")
return A()
}
}
class Bar : Foo {
func foo() -> B {
print("Bar")
return B()
}
}
let foo: Foo = Bar()
let bar = Bar()
foo.foo() // prints Foo
bar.foo() // prints Bar
What protocol should T complies with to pass this error?
class func magnitude<T: BinaryFloatingPoint>(_ n: T) where T: CVarArg {
// …
let p = Int(log10(n))
// …
}
The error is as follows:
Cannot invoke 'log10' with an argument list of type '(T)'
Based on Martin R tips, I came up with the following (I added fictional code so that one can compile it), which works nice:
protocol FloatingPointMathType : BinaryFloatingPoint, CVarArg {
var _log10Value : Self { get }
}
extension Float: FloatingPointMathType {
var _log10Value : Float {return log10(self)}
}
extension Double: FloatingPointMathType {
var _log10Value : Double {return log10(self)}
}
extension CGFloat: FloatingPointMathType {
var _log10Value : CGFloat {return log10(self)}
}
func log10<T:FloatingPointMathType>(_ x:T) -> T {return x._log10Value}
class Format {
class func magnitude<T: BinaryFloatingPoint>(_ n: T) -> Int {
let p = Int(log10(n))
return p
}
}
let d: Double = 12.0
print(Format.magnitude(d))
Goal
I want to extend basic types like Int, Double, Float... with more flexible properties and make it presentable in a chart on my app. For example, I made a chart draw that is suitable only for displaying Intbut cannot really display Float. I want to make sure when I pass arguments to this view it will display correctly.
Solution
So I made a protocol (for this example made it like this):
protocol SimplyChartable {
static func max(_ dataSet: [SimplyChartable]) -> SimplyChartable
}
And then make an extension for some types:
extension Int: SimplyChartable { }
extension Double: SimplyChartable { }
extension Float: SimplyChartable { }
and so on ...
Problem
This will be all numeric types, and whenever I pass it as numeric types to a func I need to extend all extension like this:
public static func max(_ dataSet: [SimplyChartable]) -> SimplyChartable {
return (dataSet as? [Int])?.max() ?? 0
}
But for Double func will be identical.
So for min I will end up with similar function, the same for divide, adding , some other math... There is a way to write it once and reuse for every type that extends this protocol?
I found out that:
let dataType = type(of: maxValue) /* where `maxValue` is SimplyChartable*/
Will return original type as rawValue. But output of a method type(of is a Metatype and I cannot return it from function and then add two values of this type. So for example this code will not work:
let val1 = SimplyChartable(4)
let val2 = SimplyChartable(2)
let sum = val1 + val2
And how to make it work not ending up with 3 functions like this:
let val1 = SimplyChartable(4)
let val2 = SimplyChartable(2)
let sum = (val1 as! Int) + (val2 as! Int)
Since they all numeric types why don't you use Comparable?
extension SimplyChartable {
static func max<T: Comparable>(dataSet: [T]) -> T? {
return dataSet.max()
}
static func min<T: Comparable>(dataSet: [T]) -> T? {
return dataSet.min()
}
}
extension Int: SimplyChartable { }
extension Double: SimplyChartable { }
Double.max([1.2, 1.1, 1.3]) // 1.3
Int.min([12, 11, 13]) // 11
Just my two cents worth...
This isn't exactly what you've asked for, since it doesn't let you call a static function directly from a protocol metatype. But since that, AFAIK, isn't possible in Swift currently, perhaps this would be the next best thing?
extension Sequence where Element == SimplyChartable {
func max() -> SimplyChartable {
// put your implementation here
}
}
You can then call this by just:
let arr: [SimplyChartable] = ...
let theMax = arr.max()
For your situation, it's much better to use an Array extension rather than a protocol with an array parameter.
To handle each possible type of array i.e [Int], [Double] or [Float], create a wrapper enum with associated types as follows:
public enum SimplyChartableType {
case int(Int)
case float(Float)
case double(Double)
func getValue() -> NSNumber {
switch self {
case .int(let int):
return NSNumber(value: int)
case .float(let float):
return NSNumber(value: float)
case .double(let double):
return NSNumber(value: double)
}
}
init(int: Int) {
self = SimplyChartableType.int(int)
}
init(float: Float) {
self = SimplyChartableType.float(float)
}
init(double: Double) {
self = SimplyChartableType.double(double)
}
}
You can extend Array as follows:
extension Array where Element == SimplyChartableType {
func max() -> SimplyChartableType {
switch self[0] {
case .int(_):
let arr = self.map({ $0.getValue().intValue })
return SimplyChartableType(int: arr.max()!)
case .double(_):
let arr = self.map({ $0.getValue().doubleValue })
return SimplyChartableType(double: arr.max()!)
case .float(_):
let arr = self.map({ $0.getValue().floatValue })
return SimplyChartableType(float: arr.max()!)
}
}
}
Example usage is:
var array = [SimplyChartableType.double(3),SimplyChartableType.double(2),SimplyChartableType.double(4)]
var max = array.max()
And now it's a lot easier to operate on Int, Double or Float together with:
extension SimplyChartableType: SimplyChartable {
//insert functions here
static func randomFunction() -> SimplyChartableType {
//perform logic here
}
}
The above snippet is good if you need a different functionality which operates on non-Collection types.
This doesn't answer your specific question, unfortunately. Perhaps a work around to use a free function and casting.
import UIKit
protocol SimplyChartable {
func chartableValue() -> Double
}
extension Int: SimplyChartable {
func chartableValue() -> Double {
return Double(self) ?? 0
}
}
extension Double: SimplyChartable {
func chartableValue() -> Double {
return self
}
}
extension Float: SimplyChartable {
func chartableValue() -> Double {
return Double(self) ?? 0
}
}
func maxOfSimplyChartables(_ dataSet: [SimplyChartable]) -> SimplyChartable {
return dataSet.max(by: { (lhs, rhs) -> Bool in
return lhs.chartableValue() < rhs.chartableValue()
}) ?? 0
}
let chartableItem1: SimplyChartable = 1255555.4
let chartableItem2: SimplyChartable = 24422
let chartableItem3: SimplyChartable = 35555
let simplyChartableValues = [chartableItem1, chartableItem2, chartableItem3]
maxOfSimplyChartables(simplyChartableValues)
How to avoid redundant declaration of restriction in functions f0, f1, ... f10?
class SomeClass<T: UnsignedInteger> {
func f0<S: Sequence>(sequence: S) where S.Iterator.Element == T { }
func f1<S: Sequence>(sequence: S) where S.Iterator.Element == T { }
......
func f10<S: Sequence>(sequence: S) where S.Iterator.Element == T { }
}
How to declare something like this:
typealias S = Sequence where S.Iterator.Element == T
func f0(sequence: S) { }
func f1(sequence: S) { }
......
func f10(sequence: S) { }
?
I'm not sure if you can do exactly what you are asking. A typealias is a symbol that can be used in place of an existing type, it is not a restriction pattern.
You could fix the function types to that of the class declaration by adding an additional generic placehoder type (S) to the class defintion:
class SomeClass0<T: UnsignedInteger, S: Sequence> where S.Iterator.Element == T {
func f0(sequence: S) { }
func f1(sequence: S) { }
}
But that means that referencing the type is pretty ugly, unless you can infer types from an init etc..
let instance0 = SomeClass0<UInt, [UInt]>()
You could still fix the sequence type at the class level with the placeholder type, but leave out the UnsignedInteger placeholder, depending on exactly how you are using it:
class SomeClass1<S: Sequence> where S.Iterator.Element: UnsignedInteger {
typealias T = S.Iterator.Element
func f0(sequence: S) { }
func f1(sequence: S) { }
func f99(number: T) {}
}
let instance1 = SomeClass1<[UInt]>()
For example:
func f(x: Int) -> Int {
return x
}
func h(f: #escaping (Int) -> Any) {
if (f is (Int) -> Int) {
print(f(1))
} else {
print("invalid")
}
}
h(f: f)
I expect it to print out 1 but it actually prints out invalid.
There's a workaround using generics:
func intF(x: Int) -> Int {
return x
}
func stringF(x: Int) -> String {
return "\(x)"
}
func h<T>(f: (Int) -> T) {
if (T.self == Int.self) {
print(f(1))
} else {
print("invalid")
}
}
h(f: intF) // prints: 1
h(f: stringF) // prints: invalid
Using Any is almost always a sign of code smell, you should try to rely as much as possible of the type safety that Swift provides. You can achieve this by making h generic, thus verifiable at compile time.
// the overload that does the actual stuff
func h(f: #escaping (Int) -> Int) {
print(f(1))
}
// this maps to all other types
func h<T>(f: #escaping (Int) -> T) {
print("invalid")
}
h { _ in return "15" } // Invalid
h { 2 * $0 } // 2
Heck, you could even give up the generic overload, thus you'll have for free compile checks instead of runtime failures (much, much reliable and predictive)
You can rewrite h into a generic function:
func h<T>(f: #escaping (Int) -> T) {
if T.self == Int.self {
print(f(1))
} else {
print("invalid")
}
}
But the better way to to write type-specific overloads for h and a generic catch-all for the rest (if you need it at all).