Swift Protocol and Return Types on Global Functions

Swift protocol and return types on global functions

This question has the same form as the copy one, and the same solution. Make mutation an initializer rather than a method.

protocol Copyable {
init(copy: Self)
}

protocol Mutatable : Copyable {
init(byMutating: Self)
}

class C : Mutatable {
var a = 0

required init(_ a: Int) {
self.a = a
}

required init(copy: C) {
a = copy.a
}

required convenience init(byMutating: C) {
self.init(copy: byMutating)
self.a++
}
}

// These are purely for convenience
func copy<T : Copyable>(x: T) -> T {
return x.dynamicType(copy: x)
}

func mutated<T: Mutatable>(x: T) -> T {
return x.dynamicType(byMutating: x)
}

But to reiterate Mattt's point in the linked article, you can have a C(copy: x) syntax fairly conveniently, and you can have a copy(x) syntax pretty conveniently, and there is always x.dynamicType(copy: x). But you can't have a x.copy() syntax without some annoying work. You either have to duplicate func copy() -> Self { return copy(self) } in every class, or you have to create some concrete class that implements this method and C ultimately inherits from. This is currently a basic limitation of Swift. I agree with Mattt's diagnosis of possible solutions, and suspect that some kind of trait system, possibly along the lines of Scala's, will probably be added in the future.

It's worth focusing on Mattt's comment that "all of this highlights a significant tension between methods and functions in Swift." This is another way of saying that there are tensions between the object-oriented paradigm and the functional paradigm, and moving between them can create some disconnects. Languages try to paper-over that issue with various features, but there are important differences between objects with messages and properties, vs functions with data and combinators, and "getting the best of both worlds" can sometimes create some rough edges.

It's easy to forget, when comparing Swift to other languages, that there is a big difference between v0.9 and v2.11. Many things we take for granted in our favorite languages did not exist in their v1 either.


To your comment, you may be thinking that mutated is of type Self. But it's of type C, as your autocomplete indicates. As before, C is not the same as Self unless you can promise that there are no subclasses (C being either final or a struct). Swift types are resolved at compile-time, not runtime, unless you use dynamicType.

To be a little more specific, Swift looks at this line:

    let mutated = copy(self)

It notes that copy is generic on the type of its parameter, and it must construct a version of copy at compile-time to call. There is no type Self. It's just a placeholder, and must be resolved at compile-time. The type of self in this lexical scope is C. So it constructs copy<C>. But if you subclassed C, this could be the wrong function (and in this case, would be). This is very closely related to: https://stackoverflow.com/a/25549841/97337.

The fact that type autocomplete says (C) rather than C is a minor side-effect of how Swift functions and tuples work, and comes up pretty regularly, but I've yet to encounter a case where it really mattered. A Swift function like func f(x: Int, y:Int) does not actually have two parameters. It has one 2-tuple parameter of type (Int, Int). This fact is important to how the currying syntax works (see the Swift Programming Language for more on currying in Swift). So when you specialize copy, you specialized it with a 1-tuple of type (C). (Or possibly, the compiler is just trying to do that as one of various attempts, and that's just the one it reports on.) In Swift any value can be trivially exchanged for a 1-tuple of the same type. So the return of copy is actually the 1-tuple of C, written (C). I suspect that the Swift compiler will improve its messages over time to remove the extraneous parentheses, but that's why they show up sometimes.

How to define a Protocol with generic function and some View as return type

This is explicitly disallowed by the Associated Type Inference section of the Opaque Result Types proposal. From the proposal:

Associated type inference can only infer an opaque result type for a non-generic requirement, because the opaque type is parameterized by the function's own generic arguments. For instance, in:

protocol P {
associatedtype A: P
func foo<T: P>(x: T) -> A
}

struct Foo: P {
func foo<T: P>(x: T) -> some P {
return x
}
}

there is no single underlying type to infer A to, because the return type of foo is allowed to change with T.

To make this more specific to your question, in order to conform to Test, there must be exactly one type that can be assigned to Result. However, your return type is generic, so it depends on the what is passed. The actual (non-opaque) return type of navigate is:

_ConditionalContent<NavigationLink<T, Text>, EmptyView>

But T is a type parameter and changes depending on how navigate is called. So there is no one type that can be assigned to Result.

You'll need something that can return a single, non-parameterized type. For the example you've given, that's probably AnyView, which is annoying.

That said, what you've written here doesn't really feel like a protocol. It looks a lot like just a function. I'd think a lot about how many different ways navigate could be written. If everyone would implement it the same way, that's just a function. (If you give another example of a conforming type, it might help to design a better approach.)

Why must a protocol operator be implemented as a global function?

UPDATE

From the Xcode 8 beta 4 release notes:

Operators can be defined within types or extensions thereof. For example:

struct Foo: Equatable {
let value: Int
static func ==(lhs: Foo, rhs: Foo) -> Bool {
return lhs.value == rhs.value
}
}

Such operators must be declared as static (or, within a class, class final), and have the same
signature as their global counterparts. As part of this change, operator requirements declared in
protocols must also be explicitly declared static:

protocol Equatable {
static func ==(lhs: Self, rhs: Self) -> Bool
}

ORIGINAL

This was discussed on the swift-evolution list recently (2016-01-31 through 2016-02-09 so far). Here's what Chris Lattner said, regarding declaring operators in a struct or class scope:

Yep, this is a generally desirable feature (at least for symmetric operators). This would also be great to get dynamic dispatch of operators within class declarations. I don’t think we have a firm proposal nailing down how name lookup works with this though.

And later (replying to Haravikk):

What are the name lookup issues? Do you mean cases where an operator for Foo == Foo exists in more than one location?


Yes. Name lookup has to have a well defined search order, which
defines shadowing and invalid multiple definition rules.

Personally I’d just stick with what we have now, i.e- treat operator implementations within a specific class/struct as being globally
defined anyway and throw an error if the same signature is declared
more than once.


We need multiple modules to be able to define instances of an
operator, we need operators in extensions, and we need retroactive
conformance to work, as with any other member.

Swift protocol defining class method returning self

Self in a protocol is a requirement that implementations of the protocol use their own type. Since Invoice is the type you're adopting the protocol in, your implementation of FromJson should have a return type of Invoice.

How do I define generic typealias for Swift to return specific types of objects?

As mentioned in the comments, T is not defined at class-scope. You need to specify the generic type with the class declaration so it can be used across your class.

Try this:

class PersistentStoreCoordinatorMock<T: NSManagedObject>: Storageable {
var objects = [T]() // here I need to define return array
func findAll(of type: T.Type, predicate: NSPredicate) -> [T] {
findAllWasCalled = true
return objects //here I need to return this when that function was called
}
}

Is there a way to call a global function within a class that has a method whose name is the same as the global function?

Yup, there is. If you app's name were, for instance, "myApp", then you could call the global function, which has the same name and number of argument as the one in your class B, like so:

func b()
{
print(myApp.a(100))
}


Related Topics



Leave a reply



Submit