Swift generic type defined by method context - swift

Is there a name for a pattern where the type is inferred by the context of the result type?
Eg in this example what language could i use to document the foo method and explain that a type needs to be defined for the method to work?
protocol FooType {
init()
}
func foo<T: FooType>() -> T {
return T()
}
struct Bar: FooType {
init() {
print("bar")
}
}
let bar: Bar = foo()
// works returns instance of Bar
let fooType = foo()
// fails because foo doesn't know what type to use

You don't need to document this!
Everyone that writes code in Swift knows that to call a generic function, all its type parameters must be inferred and cannot be spoon-fed like this:
foo<Bar>()
People will see foo and say, "Oh I need the compiler to infer the type for this generic parameter." They will understand what this means.

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

Why can't I assign this protocol conforming class to a variable of the protocol type?

I've got a toy example here that I can't find any solutions for online.
protocol Tree {
associatedtype Element
// some nice tree functions
}
class BinaryTree<T> : Tree {
typealias Element = T
}
class RedBlackTree<T> : Tree {
typealias Element = T
}
enum TreeType {
case binaryTree
case redBlackTree
}
// Use a generic `F` because Tree has an associated type and it can no
// longer be referenced directly as a type. This is probably the source
// of the confusion.
class TreeManager<E, F:Tree> where F.Element == E {
let tree: F
init(use type: TreeType){
// Error, cannot assign value of type 'BinaryTree<E>' to type 'F'
switch(type){
case .binaryTree:
tree = BinaryTree<E>()
case .redBlackTree:
tree = RedBlackTree<E>()
}
}
}
I'm not sure what the problem here is or what I should be searching for in order to figure it out. I'm still thinking of protocols as I would interfaces in another language, and I view a BinaryTree as a valid implementation of a Tree, and the only constraint on F is that it must be a Tree. To confuse things even more, I'm not sure why the following snippet compiles given that the one above does not.
func weird<F:Tree>(_ f: F){ }
func test(){
// No error, BinaryTree can be assigned to an F:Tree
weird(BinaryTree<String>())
}
Any pointers or explanations would be greatly appreciated.
I don't understand the context of the situation this would be in. However, I have provided two solutions though:
1:
class Bar<F:Foo> {
let foo: FooClass.F
init(){
foo = FooClass.F()
}
}
2:
class Bar<F:Foo> {
let foo: FooClass
init(){
foo = FooClass()
}
}
What you are currently doing doesn't make logical sense, to whatever you are trying to achieve
I don't know what are you trying for, but of course it's not possible to do that. from your example, you attempt to create Bar class with generic. and that not the appropriate way to create a generic object, because the creation of generic object is to make the object to accept with any type.
Here's some brief explanation of the generic taken from Wikipedia.
On the first paragraph that says, "Generic programming is a style of computer programming in which algorithms are written in terms of types to-be-specified-later that are then instantiated when needed for specific types provided as parameters."
it's very clear about what are the meaning of to-be-specified-later, right :)
Back to your example :
class Bar<F: Foo> {
let foo: F
init() {
// Error, cannot assign value of type 'FooClass' to type 'F'
foo = FooClass()
}
}
From the above code, it is type parameter F which has a constraint to a type Foo. and, you try to create an instance for foo variable with a concrete implementation, that is FooClass. and that's not possible since the foo variable is a F type(which is abstract). of course we can downcast it, like this foo = FooClass() as! F, but then the foo is only limited to FooClass, so why even bother with generic then ?
Hope it help :)
Your approach to this is wrong. In your original example, you would have to specify the type of both the element (E) and the container (BinaryTree or RedBlackTree), in addition to the enum value. That make no sense.
Instead, you should construct the manager to take a tree as the constructor argument, allowing Swift to infer the generic arguments, i.e.
class TreeManager<E, F: Tree> where F.Element == E {
var tree: F
init(with tree: F) {
self.tree = tree
}
}
let manager = TreeManager(with: BinaryTree<String>())
Alternatively, you should look into using opaque return types in Swift 5.1 depending on what the final goal is (the example here is obviously not a real world scenario)
Something like this seems reasonable. The point was to try and have some piece of logic that would determine when the TreeManager uses one type of Tree vs another.
protocol TreePicker {
associatedtype TreeType : Tree
func createTree() -> TreeType
}
struct SomeUseCaseA<T>: TreePicker {
typealias TreeType = RedBlackTree<T>
func createTree() -> TreeType {
return RedBlackTree<T>()
}
}
struct SomeUseCaseB<T>: TreePicker {
typealias TreeType = BinaryTree<T>
func createTree() -> TreeType {
return BinaryTree<T>()
}
}
class TreeManager<T, Picker: TreePicker> where Picker.TreeType == T {
let tree: T
init(use picker: Picker){
tree = picker.createTree()
}
This introduces another protocol that cares about picking the tree implementation and the where clause specifies that the Picker will return a tree of type T.
I think all of this was just the result of not being able to declare the tree of type Tree<T>. Its a bit more verbose but its basically what it would have had to look like with a generic interface instead. I think I also asked the question poorly. I should have just posted the version that didn't compile and asked for a solution instead of going one step further and confusing everyone.
protocol Foo {
associatedtype F
}
class FooClass : Foo {
typealias F = String
}
class Bar<M:Foo> {
let foo: M
init(){
foo = FooClass() as! M
}
}

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

Protocol extensions on Structs causes compile error 'Self' constrained to non-protocol type

I'm attempting to apply a constrained protocol extension to a struct (Swift 2.0) and receiving the following compiler error:
type 'Self' constrained to non-protocol type 'Foo'
struct Foo: MyProtocol {
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
protocol MyProtocol {
func bar()
}
extension MyProtocol where Self: Foo {
func bar() {
print(myVar)
}
}
let foo = Foo(myVar: "Hello, Protocol")
foo.bar()
I can fix this error by changing struct Foo to class Foo but I don't understand why this works. Why can't I do a where Self: constrained protocol a struct?
This is an expected behaviour considering struct are not meant to be inherited which : notation stands for.
The correct way to achieve what you described would be something like equality sign like:
extension MyProtocol where Self == Foo {
func bar() {
print(myVar)
}
}
But this doesn't compile for some stupid reason like:
Same-type requirement makes generic parameter Self non-generic
For what it's worth, you can achieve the same result with the following:
protocol FooProtocol {
var myVar: String { get }
}
struct Foo: FooProtocol, MyProtocol {
let myVar: String
}
protocol MyProtocol {}
extension MyProtocol where Self: FooProtocol {
func bar() {
print(myVar)
}
}
where FooProtocol is fake protocol which only Foo should extend.
Many third-party libraries that try to extend standard library's struct types (eg. Optional) makes use of workaround like the above.
I just ran into this problem too. Although I too would like a better understanding of why this is so, the Swift language reference (the guide says nothing about this) has the following from the Generic Parameters section:
Where Clauses
You can specify additional requirements on type parameters and their
associated types by including a where clause after the generic
parameter list. A where clause consists of the where keyword, followed
by a comma-separated list of one or more requirements.
The requirements in a where clause specify that a type parameter
inherits from a class or conforms to a protocol or protocol
composition. Although the where clause provides syntactic sugar for
expressing simple constraints on type parameters (for instance, T:
Comparable is equivalent to T where T: Comparable and so on), you can
use it to provide more complex constraints on type parameters and
their associated types. For instance, you can express the constraints
that a generic type T inherits from a class C and conforms to a
protocol P as <T where T: C, T: P>.
So 'Self' cannot be a struct or emum it seems, which is a shame. Presumably there is a language design reason for this. The compiler error message could certainly be clearer though.
As Foo is an existing type, you could simply extend it this way:
struct Foo { // <== remove MyProtocol
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
// extending the type
extension Foo: MyProtocol {
func bar() {
print(myVar)
}
}
From The Swift Programming Language (Swift 2.2):
If you define an extension to add new functionality to an existing type, the new functionality will be available on all existing instances of that type, even if they were created before the extension was defined.

Can I cast a metaclass object to a protocol type in Swift?

Swift inherited Objective-C's metaclass concept: classes themselves are also considered objects. A class Foo's object's class is Foo.self, and it is of type Foo.Type. If Foo inherits from Bar, then Foo.self can be assigned to a variable of type Bar.Type, too. This has at least two benefits:
it allows to override "static methods";
it's easy to create an instance of an unknown class in a type-safe way and without using reflection.
I'm particularly looking at the second one right now. Just to be sure that everybody understands what I'm after, here's an example:
class BaseFoo {
var description: String { return "BaseFoo" }
}
class DerivedFoo: BaseFoo {
override var description: String { return "DerivedFoo" }
}
let fooTypes: [BaseFoo.Type] = [BaseFoo.self, DerivedFoo.self] // metaclass magic!
for type in fooTypes {
let object: BaseFoo = type() // metaclass magic!
println(object)
}
Now, I have an array of AnyClass objects (any metaclass instance can be assigned to AnyClass, just like any object can be assigned to AnyObject), and I want to find which ones implement a given protocol. The protocol would declare an initializer, and I would instantiate the class just like I do in the example above. For instance:
protocol Foo {
init(foo: String)
}
class Bar: Foo {
required init(foo: String) { println("Bar initialized with \(foo)") }
}
class Baz {
required init() { println("I'm not a Foo!") }
}
let types: [AnyClass] = [Bar.self, Baz.self]
So far so good. Now, the problem is determining if the class implements the protocol. Since metaclass instances are polymorphic, I'd expect to be able to cast them. However, I'm apparently missing something, because Swift won't let me write this:
for type in types {
if let fooType = type as? Foo.Type {
let obj = fooType(foo: "special snowflake string")
}
}
The compiler error I get is:
error: 'Foo' is not identical to 'AnyObject'
Is there any way to determine if a metaclass instance represents a class that implements a protocol, and is there any way to cast that instance into a protocol type?
I tried to declare Foo as a class protocol, but it's apparently not enough.
EDIT: I just tried with the Any type, and while it doesn't cause a syntax error, it crashes the Swift compiler.
As of Xcode 7 beta 2 and Swift 2 it has been fixed. You can now write:
for type in types {
if let fooType = type as? Foo.Type {
// in Swift 2 you have to explicitly call the initializer of metatypes
let obj = fooType.init(foo: "special snowflake string")
}
}
Or if you only want type as type Foo.Type you can use for case
for case let type as Foo.Type in types {
let obj = type.init(foo: "special snowflake string")
}