Default parameter as generic type - swift

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.

Related

Implement generic protocol method with but use generic for whole class

I'm am trying to implement a protocol method that has a generic argument, but then use the generic type for my entire class instead of just on the method, something like this
protocol FirstProtocol {
}
protocol SecondProtocol {
func foo<T: FirstProtocol>(argument: T)
}
class MyType<T: FirstProtocol>: SecondProtocol {
var value: T? = nil
func foo<T>(argument: T) {
value = argument // ERROR: Cannot assign value of type 'T' to type 'T?'
}
}
So the swift compiler accepts that foo<T>(argument:T) matches the method of SecondProtocol, if I comment out the error line it compiles fine, but it will not let me assign argument to value even though value and argument should be the same type, the compiler complains as if they are different types.
The type of argument and value are indeed different types. The T generic parameter in foo is just an identifier, and I can change it to anything else:
class MyType<T: FirstProtocol>: SecondProtocol {
var value: T? = nil
func foo<AnythingElse>(argument: AnythingElse) {
// MyType still conforms to SecondProtocol
}
}
The T in foo is a brand new generic parameter, different from the T in MyType. They just so happens to have the same name.
Note that when you declare a generic method, it's the caller that decides what the generic type is, not the generic method. What foo is trying to say here is "I want the T in foo to be the same type as the T in MyType", but it can't say that about its own generic parameters!
One way to fix it is to make SecondProtocol have an associated type:
protocol SecondProtocol {
// name this properly!
associatedtype SomeType: FirstProtocol
func foo(argument: SomeType)
}
class MyType<T: FirstProtocol>: SecondProtocol {
typealias SomeType = T // here is where it says "I want 'SomeType' to be the same type as 'T'!"
var value: T? = nil
func foo(argument: T) {
value = argument
}
}
it will not let me assign argument to value even though value and argument should be the same type, the compiler complains as if they are different types.
think about this case:
class A: FirstProtocol {
}
class B: FirstProtocol {
}
class A and B is the acceptable generic type for func foo(argument: T){}, but can you assign an instance of class A to class B?
class MyType<T: FirstProtocol>: SecondProtocol
remove ": FirstProtocol"should work, or use a base class to replace FirstProtocol

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

Extending custom type where associated type is equal to Void

I'm in a situation where I have a custom type that contains an associatedtype. In the case where this is equal to Void, I would like to have some default behaviour (to make the call-site more convenient). I tried to boil the example down to:
protocol FooType {
associatedtype T: Any
var bar: (String) -> T { get }
}
struct Foo<T>: FooType {
let bar: (String) -> T
}
extension Foo where T == Void { // Compile error: "Same-type requirement makes generic parameter 'T' non-generic".
init() {
self.bar = { _ in return }
}
}
The idea is that, in the cases where the generic type is Void, it doesn't make sense (in my scenario) to pass in a function (named bar in the example). Therefore, I just want a default implementation for this function in this specific context.
When trying to do the above I get the Same-type requirement makes generic parameter 'T' non-generic which sounds very similar to what happens when one tries to restrict e.g. the Array type when containing specific types. A workaround for this is to introduce a protocol, but I cannot do that for Void. Is it possible to do what I want or is this currently a limitation in Swift 3?
As of Swift 3.1, the code posted in the question now works. That is, the following now works as wanted:
protocol FooType {
associatedtype T: Any
var bar: (String) -> T { get }
}
struct Foo<T>: FooType {
let bar: (String) -> T
}
extension Foo where T == Void {
init() {
self.bar = { _ in return }
}
}
let foo = Foo<String>(bar: { (t: String) in return "" })
let zoo = Foo<Void>()

Make function of generic class only accept argument that is same generic type but more constrained

I have a generic class that can be initialised as any type. I would like to add a function with a single parameter that takes a value that is both of the class's generic type and conforms to the Comparable protocol. Type conformance should be enforced pre-compile.
I would like to do something like this:
class Object<T> {
let value: T!
init (value: T) {
self.value = value
}
func doSomething<U where U: Comparable, U == T>(otherValue: U) {
// do something
}
}
Is this possible to do?
Unfortunately, no. You can't further specialize a generic type in a method - you'd need to add a top-level function for the behavior you want.
This is the reason Array doesn't have a pure myArray.sort() function, since there's no way to guarantee that the members of any Array instance will be Comparable. Instead, there's a top-level function with this signature:
func sort<T : Comparable>(inout array: [T])
Your top-level function would have a similar structure:
func doSomething<T: Comparable)(obj: Object<T>, otherValue: T) {
// ...
}
For reference, this is possible since Swift 2.0:
extension Object where T : Comparable {
func doSomething(otherValue: T) {
// do something
}
}

Error using associated types and generics

The following code gives me an error on line return p.foo(self). The error says: 'P' does not have a member named 'foo'.
protocol P {
typealias T
func foo(c: C<T>) -> T
func foo2() -> T
}
class C<T> {
var p: P
init (p: P) {
self.p = p
}
func bar() -> T {
return p.foo(self);
}
}
The protocol P defines an associated type which should match correctly with any specialized C type. Am I missing something? Or not?
I'll rename the types a bit before answering the question to make the problem a bit clearer:
protocol P {
typealias ElementType
func foo(c: C<ElementType>) -> ElementType
func foo2() -> ElementType
}
class C<T> {
var p: P
init (p: P) {
self.p = p
}
func bar() -> T {
return p.foo(self)
}
}
In that case you get three compiler errors:
error: <EXPR>:8:12: error: protocol 'P' can only be used as a generic constraint because it has Self or associated type requirements
var p: P
^
<EXPR>:9:14: error: protocol 'P' can only be used as a generic constraint because it has Self or associated type requirements
init (p: P) {
^
<EXPR>:13:16: error: 'P' does not have a member named 'foo'
return p.foo(self)
^ ~~~
The interesting one is the first/second one (they point out the same problem): "protocol 'P' can only be used as a generic constraint because it has Self or associated type requirements".
So the problem is the associated type. In the current configuration, you specify that the parameter for the initializer and the variable are of type P. But because you specified an associated type for P, that type is not specific enough to be used as a proper type. Only subtypes that actually specify what the ElementType is can be used. However, you can specific a generic parameter that has to be a subtype of P. In the case of the initializer you could write
init <S:P>(p: S) {
self.p = p
}
which would eliminate the compiler error for the initializer. Now the compiler knows the parameter has to be a subtype of P and a valid subtype always specifies what the ElementType is, so it is happy.
But this doesn't help you with this line:
var p: P
You still can't use the incomplete type P here. You would probably want to use S, but at the moment there is no connection between the S in the initializer and an S you would use as the type for you variable but they obviously need to be the same.
Time to introduce a second generic parameter to your class:
class C<T, S:P> {
var p: S
init (p: S) {
self.p = p
}
func bar() -> T {
return p.foo(self)
}
}
Almost done, now you have a properly specified type to use for your variable. But no your protocol specification is incorrect:
func foo(c: C<ElementType>) -> ElementType
C now takes two parameters and you need to specify these here. We would like to use `C here, but we can't:
error: :3:17: error
: type 'P' does not conform to protocol 'P'
func foo(c: C<ElementType, P>) -> ElementType
^
<EXPR>:2:15: note: associated type 'ElementType' prevents protocol from conforming to itself
typealias ElementType
Since P does not specify the associated type ElementType it does not properly conform to P and can't be used in a place where a type conforming to P is needed. But there is a nice special type: Self. That references the actual type of the implementing protocol, so we can write the following:
protocol P {
typealias ElementType
func foo(c: C<ElementType, Self>) -> ElementType
func foo2() -> ElementType
}
Now we specified that the foo-function that is implemented by any confirming type actually takes a C with the specified ElementType and the implementing type itself. Fancy, isn't it?
But we aren't fully done yet, one last error remains:
error: <EXPR>:13:18: error: cannot convert the expression's type 'T' to type 'S.ElementType'
return p.foo(self)
At this point the compiler knows the following:
p.foo(self) returns something of the ElementType of S
The function bar() should return something of type T
But there is nothing to tell it, that ElementType and T are actually the same, so it can't be sure whether this works and complains. So what we actually want is that the ElementType of S is always the same as T and we can specify this:
class C<T, S:P where S.ElementType == T> {
Complete code:
protocol P {
typealias ElementType
func foo(c: C<ElementType, Self>) -> ElementType
func foo2() -> ElementType
}
class C<T, S:P where S.ElementType == T> {
var p: S
init (p: S) {
self.p = p
}
func bar() -> T {
return p.foo(self);
}
}
You never required that the two Ts match. They are each in their own scope. Therefore the compiler looks for a foo function with the wrong parameter types.
I believe something like this would be correct:
protocol P {
typealias T
func foo<ConcreteP>(c: C<T, ConcreteP>) -> T
func foo2() -> T
}
class C<T, ConcreteP: P where T == ConcreteP.T> {
var p: ConcreteP
init (p: ConcreteP) {
self.p = p
}
func bar() -> T {
return p.foo(self);
}
}
At least I don't get any syntax errors. However on compilation I get:
error: unimplemented IR generation feature non-fixed class layout
var p: ConcreteP
^
LLVM ERROR: unimplemented IRGen feature! non-fixed class layout
That sounds like a compiler bug.
From The Swift Programming Language:
Protocols do not actually implement any functionality themselves. Nonetheless, any protocol you create will become a fully-fledged type for use in your code.
In order to use the .foo method, you need a struct or class that implements your protocol.