Swift 2.0 Generics and Type Safety Issues - swift

I was doing this tutorial http://blog.teamtreehouse.com/introduction-learn-power-swift-generics and I came upon this code;
func someFunction<T, U>(a: T, b: U) {}
The problem is when I call the function using
someFunction<String, Int>(1, "Test")
I get an error saying "cannot explicitly specialize a generic function".
I then change it to
someFunction(1,b: "Test")
and now there is no error. The problem is that there is now no type safety. (Is there something wrong with the code, as it was written before swift 2.0?) What is the best way to re-introduce type safety?

The declaration is completely generic and is specifying that any two types can be used.
func someFunction<T, U>(a: T, b: U) {}
It is not that there is no type safety in Swift, this is how you express a generic without any type constraints.
You get what you ask for.
If you wanted to constrain the function to String and Int, you would have written it as
func someFunction(a:String, b:Int)
Generics are more often used with collections, protocols and classes. Basic types rarely need them :
func someFunction<T:Comparable, U:Comparable>(a:T, b:U) -> Bool
{ return (a < b) || (a > b) }

ok, see this 'self explanatory' example. try it in playground and play with it a little bit.
func foo<T>(t: T)->T {
return t
}
// here the resulting type is infered by compiler
let i = foo(1) // 1
let j: Int = foo(1) // 1
let t = foo("alpha") // "alpha"
// if you declare it self ..
//let s: String = foo(1) // error: cannot convert value of type 'Int' to expected argument type 'String'
/* this fails to compile!
func bar<T>(t:T)->Int {
return t.count
}
*/
/* this fails to compile too !!
func bar0<T:CollectionType>(t:T)->Int {
return t.count
}
*/
func bar<T:CollectionType>(t:T)->T.Index.Distance {
return t.count
}
let arr = [1,2,3]
let k:Int = bar(arr) // 3
print(k.dynamicType) // Int
// and even more interesting
let l = bar(arr)
print(l.dynamicType) // Int

Related

Getting error: String is not convertible to 'T'

This code compiles and works fine:
class Numbers {
func operateOn<T>(_ num1: T, _ num2: T, do task: (T, T) -> ()) {
task(num1, num2)
}
}
let n = Numbers()
n.operateOn(1,2) {
print(($0 + $1) * 10)
}
n.operateOn("l","ll") {
print(($0 + $1))
}
Yet for for following code does not compile.
func process<T> (add: String, completion: (T) -> () ) {
completion("k") // ERROR
}
Yet I get the following error:
'String' is not convertible to 'T'
I tried passing an Int, but I just got another error:
'Int' is not convertible to 'T'
Can't an Int or a String satisfy a generic requirement that doesn't have any constraints?!
The problem is that your code needs to work for any T. E.g. if T is Int then completion has type (Int) -> (), it's completely legitimate to call
n.process<Int>("") { $0 + 1 }
and completion("k") would have to do "k" + 1 which doesn't make sense.
This is going to be the same in basically any language with generics (C++ is different because it uses templates for the same purpose instead).
Can't an Int or a String satisfy a generic requirement that
doesn't have any constraints?!
Sure it can. But that's not the reason the compiler is giving you an error.
Think about it, what happens if you constrain a generic function/parameter within the function body itself?! It will no longer be a generic function!
Imagine if you had wrote your operateOn function as such:
class Numbers {
func operateOn<T>(_ num1: T, _ num2: T, do task: (T, T) -> ()) {
task("k", num2) // add two strings
}
}
Would you say that T is generic? Or that it's of type String? If you made it a String then can num2 be any generic type it wants to be? It can't!
If it's of type String then it's no longer generic. Since the compiler can't allow that it will throw that error.

How can I test if an object inherits or is a member of a generic class?

Suppose I have a class class C<T>.
I want to write a function f(_ a: Any) -> Bool, which returns true when a is a member of class inheriting from C (or is a C itself). I don't care about the specialization: passing C<Int>, C<Double>, C<Whatever> should all return true.
It seems like I should be able to just write a is C or a as? C != nil as the body of that function, however, these both don't compile in a playground; swiftc complains that "generic parameter 'T' could not be inferred in cast to 'C<_>'". If I write f inside of C as an instance method, C is implicitly C<T>, and so writing let c = C<Int>(); c.f(C<Double>()) returns false.
I can work around this by writing a protocol P, which C conforms to, and then test for that but I don't consider that a good solution; it's just a hack.
Is there any way to do this?
Here's all the code I wrote to try this:
class C<T> {
func test(_ a: Any) -> (Bool, Bool) {
return (type(of: a) == C.self, a is C)
}
}
class D: C<Double> { }
let d = D()
func f(_ a: Any) -> Bool {
return a is C // generic parameter 'T' could not be inferred in cast to 'C<_>'
}
d.test(C<Int>()) // false, false
// bad solution
protocol P { }
extension C: P { }
d is P // true
I don't think a language feature exists for you to do this, because it's not useful to just know that an object is a subclass of, or C itself, without knowing the generic type parameters.
I mean, what are you gonna do after knowing that, yes, someObj is indeed of type C<Something>, but not what Something is? You can't do anything with someObject. You can't cast it so you can't access any members of it.
Swift's generics is very very strict, unlike Java, who just throws generic types out of the window at runtime...
If you insist that you want to do this, your only option is to use the hack you mentioned.
Or, if you just want to check whether a is C itself, not any of its subclasses, you can use this (just for fun):
func f(a: Any) -> Bool {
let regex = try! NSRegularExpression(pattern: "C<.+>")
let typeString = String(describing: type(of: a))
let range = NSRange(location: 0, length: typeString.characters.count)
let matchRange = regex.rangeOfFirstMatch(in: typeString, range: range)
return range.toRange() == matchRange.toRange()
}
Writing a protocol that specifies what you are expecting for this test is not at all a hack; it's exactly how Swift should be used. (What you are asking is indeed impossible, and purposefully so.) Write that protocol, but not as an empty hack; fill it with what you are testing for and then view it as a beautiful part of your code's structure!
See how dump is declared
func dump<T, TargetStream where TargetStream : TextOutputStream>(_ value: T, to target: inout TargetStream, name: String? = default, indent: Int = default, maxDepth: Int = default, maxItems: Int = default) -> T
And check what it produces
class C<T>{}
class D:C<Double>{}
let c = C<Void>()
let d = D()
dump(c)
dump(d)
Yes, dump is part of Swift's standard library ... which use reflection (objects mirror) to produce its result.
UPDATE
I created cli project named dumptest with one file main.swift
//
// main.swift
// dumptest
//
class C<T>{}
class D:C<Double>{}
class E:D{}
let c = C<Void>()
let d = D()
let e = E()
var tc = ""
var td = ""
var te = ""
dump(c, to: &tc)
dump(d, to: &td)
dump(e, to: &te)
print("type info about c:", tc)
print("type info about d:", td)
print("type info about e:", te)
running it the program reported
type info about c: - dumptest.C<()> #0
type info about d: - dumptest.D #0
- super: dumptest.C<Swift.Double>
type info about e: - dumptest.E #0
- super: dumptest.D
- super: dumptest.C<Swift.Double>
Program ended with exit code: 0
For parsing String variables check stackoverflow, or ask a new question ...
lets have
import Cocoa
let v = NSView()
dump(v)
dump value v and we have
- <NSView: 0x100a022a0> #0
- super: NSResponder
- super: NSObject
Program ended with exit code: 0
If you need something "more complex", you can play with
Mirror

Return any type from a function in Swift

I am attempting to create a function that can return any type. I do not want it to return an object of type Any, but of other types, i.e. String, Bool, Int, etc. You get the idea.
You can easily do this using generics in this fashion:
func example<T>(_ arg: T) -> T {
// Stuff here
}
But is it possible to do it without passing in any arguments of the same type? Here is what I am thinking of:
func example<T>() -> T {
// Stuff here
}
When I try to do this, everything works until I call the function, then I get this error:
generic parameter 'T' could not be inferred
is it possible to do it without passing in any arguments of the same type?
The answer is yes, but there needs to be a way for the compiler to infer the correct version of the generic function. If it knows what it is assigning the result to, it will work. So for instance, you could explicitly type a let or var declaration. The below works in a playground on Swift 3.
protocol Fooable
{
init()
}
extension Int: Fooable {}
extension String: Fooable {}
func foo<T: Fooable>() -> T
{
return T()
}
let x: String = foo() // x is assigned the empty string
let y: Int = foo() // y is assigned 0

Collection of <type implementing protocol> in Swift [duplicate]

As an exercise in learning I'm rewriting my validation library in Swift.
I have a ValidationRule protocol that defines what individual rules should look like:
protocol ValidationRule {
typealias InputType
func validateInput(input: InputType) -> Bool
//...
}
The associated type InputType defines the type of input to be validated (e.g String). It can be explicit or generic.
Here are two rules:
struct ValidationRuleLength: ValidationRule {
typealias InputType = String
//...
}
struct ValidationRuleCondition<T>: ValidationRule {
typealias InputType = T
// ...
}
Elsewhere, I have a function that validates an input with a collection of ValidationRules:
static func validate<R: ValidationRule>(input i: R.InputType, rules rs: [R]) -> ValidationResult {
let errors = rs.filter { !$0.validateInput(i) }.map { $0.failureMessage }
return errors.isEmpty ? .Valid : .Invalid(errors)
}
I thought this was going to work but the compiler disagrees.
In the following example, even though the input is a String, rule1's InputType is a String, and rule2s InputType is a String...
func testThatItCanEvaluateMultipleRules() {
let rule1 = ValidationRuleCondition<String>(failureMessage: "message1") { $0.characters.count > 0 }
let rule2 = ValidationRuleLength(min: 1, failureMessage: "message2")
let invalid = Validator.validate(input: "", rules: [rule1, rule2])
XCTAssertEqual(invalid, .Invalid(["message1", "message2"]))
}
... I'm getting extremely helpful error message:
_ is not convertible to ValidationRuleLength
which is cryptic but suggests that the types should be exactly equal?
So my question is... how do I append different types that all conform to a protocol with an associated type into a collection?
Unsure how to achieve what I'm attempting, or if it's even possible?
EDIT
Here's it is without context:
protocol Foo {
typealias FooType
func doSomething(thing: FooType)
}
class Bar<T>: Foo {
typealias FooType = T
func doSomething(thing: T) {
print(thing)
}
}
class Baz: Foo {
typealias FooType = String
func doSomething(thing: String) {
print(thing)
}
}
func doSomethingWithFoos<F: Foo>(thing: [F]) {
print(thing)
}
let bar = Bar<String>()
let baz = Baz()
let foos: [Foo] = [bar, baz]
doSomethingWithFoos(foos)
Here we get:
Protocol Foo can only be used as a generic constraint because it has
Self or associated type requirements.
I understand that. What I need to say is something like:
doSomethingWithFoos<F: Foo where F.FooType == F.FooType>(thing: [F]) {
}
Protocols with type aliases cannot be used this way. Swift doesn't have a way to talk directly about meta-types like ValidationRule or Array. You can only deal with instantiations like ValidationRule where... or Array<String>. With typealiases, there's no way to get there directly. So we have to get there indirectly with type erasure.
Swift has several type-erasers. AnySequence, AnyGenerator, AnyForwardIndex, etc. These are generic versions of protocols. We can build our own AnyValidationRule:
struct AnyValidationRule<InputType>: ValidationRule {
private let validator: (InputType) -> Bool
init<Base: ValidationRule where Base.InputType == InputType>(_ base: Base) {
validator = base.validate
}
func validate(input: InputType) -> Bool { return validator(input) }
}
The deep magic here is validator. It's possible that there's some other way to do type erasure without a closure, but that's the best way I know. (I also hate the fact that Swift cannot handle validate being a closure property. In Swift, property getters aren't proper methods. So you need the extra indirection layer of validator.)
With that in place, you can make the kinds of arrays you wanted:
let len = ValidationRuleLength()
len.validate("stuff")
let cond = ValidationRuleCondition<String>()
cond.validate("otherstuff")
let rules = [AnyValidationRule(len), AnyValidationRule(cond)]
let passed = rules.reduce(true) { $0 && $1.validate("combined") }
Note that type erasure doesn't throw away type safety. It just "erases" a layer of implementation detail. AnyValidationRule<String> is still different from AnyValidationRule<Int>, so this will fail:
let len = ValidationRuleLength()
let condInt = ValidationRuleCondition<Int>()
let badRules = [AnyValidationRule(len), AnyValidationRule(condInt)]
// error: type of expression is ambiguous without more context

Default parameter as generic type

I have protocol and his implementation written in Swift:
protocol P {
}
struct A: P {
}
Protocol is used as generic type for some function:
func foo<T: P>(param: T) {
}
func foo() {
foo(param: A())
}
Until now everything works properly. But I would like to set A() as a default parameter of given function:
func foo<T: P>(param: T = A()) {
}
Unfortunately with following error:
Default argument value of type 'A' cannot be converted to type 'T'.
Or
func foo<T: P>(param: T = A() as P) {
}
,
let a: P = A()
func foo<T: P>(param: T = a) {
}
Returns:
Default argument value of type 'P' cannot be converted to type 'T'
Or
func foo<T: P>(param: T = A() as T) {
}
Returns:
'A' is not convertible to 'T'; did you mean to use 'as!' to force downcast?
What I'm doing wrong? Where is the problem?
I do not want to use force cast like this:
func foo<T: P>(param: T = A() as! T) {
}
Thank you in advance.
You're trying to enforce a non-generic default argument in a generic function: you should probably think over what you're trying to achieve here.
For the sake of the discussion, you could include an attempted cast of A() to T in your function signature, but you'd need to change the argument type to optional to allow failed conversion (nil), e.g.
func foo<T: P>(param: T? = (A() as? T)) { }
A more sound alternative is including - in addition to your generic function - a concrete non-generic function for instances where T is A (concrete functions will take precedence over generic ones), in which case you can include the default argument of A() in the function signature of the concrete function. E.g.
protocol P { }
struct A: P { }
extension Int: P { }
func foo<T: P>(param: T) { print("called generic foo") }
func foo(param: A = A()) { print("called A specific foo") }
foo() // called A specific foo (making use of default arg)
foo(A()) // called A specific foo
foo(1) // called generic foo
Note that the non-generic foo is called even though A conforms to P (A could've made use of the generic foo): there's no conflict here as the concrete function takes precedence.
If you, on the other hand, just want your generic function to allow calling without the single argument (i.e., making use of a default argument), you can include a blueprint of a simple initializer in P, allowing you to initialize an instance of the generic type as default argument; see #Sulthan:s answer.
The only thing you need is to add a requirement for an initializer to the protocol:
protocol P {
init()
}
struct A: P {
var x: Int
init() {
x = 10
}
}
func foo<T: P>(param param: T = T()) {
}
However, you will have another problem. The type of the passed parameter decides the type of the generic so you will have to specify the generic type somehow else.