Suppose I want to write a generic function that calculates average and returns a double, that accepts types that can be convertible to double. Is there a way to specify this as a protocol?
Suppose I want something like
protocol ConvertibleToDouble {
// requires Double.init(self) - how to implement it?
}
//
// ...
//
func <C: Collection>(_ collection: C) -> Double where C.Element: ConvertibleToDouble {
// average code that uses sum += Double(collection[i]) expression
}
Is there a way to implement it in this way?
Of course, there are workarounds, for example
and writing default extensions for types that are accepted by Double.init but it requires to do so for every protocol and every type for which I've defined the conversion, but I am curious whether there are more strightforward ways to do it, that require less boilerplate.
In general, Double.int can be replaced by any requirement for existence of a function necessaryFunction(_ item: MyProtocol) -> T or a similar form.
It's just an example.
If for example, one has a bunch of user defined types that convert to Double (or another type, or in a more general have some utility function defined on them), is there an easy to express such requirement? Of course, one can always define methods that wrap these calls and express them as a part of the protocol which works. The question is whether writing these wrappers is the only way to do it.
Abstraction over protocol in the general case seems fine and unavoidable in either way. Like, you can organize that in other ways, e.g. via wrapper type that accepts for initialization all supported types, but apart from not using protocol here, you still have to write a lot of such code.
You can also make your function accept a closure as a second parameter transform: (C.Element) -> Double to left this up to the user of the function. If majority of cases covetable by single implementation, it can be default value, then each time it needs custom processing — just redefine the closure.
I don’t think there is universal solution, just whatever fits better in the scenario. And you definitely wouldn’t be able to avoid explicitly defining transformations in one way or another.
I'm not sure I understand. You want to write a requirement for a type such that a Double can be constructed from that type, with an init call. But Double already has some specific init overloads, and adding a new one, related to the requirement you want to write, is an extension on Double, not a requirement of a new protocol.
thus, declaring a new member of a type (such as an init) is an extension on that type, not a new protocol.
Things look better if necessaryFunction is a free function, because we can make it a member of a generic type by declaring it as a static func taking Self
protocol MyProtocol {
static func necessaryFunction<T>(_: Self) -> T
}
To go back to ConvertibleToDouble, but in this form, it could look like this:
Again - suppose you have an overloaded function defined on several unrelated types. Just to make the example more concrete - our function is "convert to double" aka Double.init but it can be literally anything. Now, I want to write a generic code that applies this function to every type it was overloaded with. Is there a way to define this requirement directly, or one needs to write a wrapper? (for example self.toDouble() if our function is Double.init(_:)).
It sounds like you're asking for a where clause requirement that states that some type parameter T is one for which there is a Double.init(T) overload.
It is fun to imagine a generics system based entirely on these kinds of "structural" requirements -- instead of methods inside a protocol, you abstract over arbitrary overload sets. A few reasons come to mind why this isn't a good fit for Swift though:
The set of visible overloads of a given name depends on the imports of the current file; you'd have to be careful to define what it means for T to satisfy this requirement in a given lexical scope, and how that translates to other scopes. Presumably, the requirement would have to desugar to a sum type, eg T == Int or Float or Double or ....
To actually call your overload dynamically, the compiler would have to generate an entry point that takes an arbitrary value and dispatches to the correct Double.init implementation at runtime. In the general case you'd have multiple arguments you can dispatch over.
While all of the Double.init overloads have the same type (except for optionality), in the general case overloads of a name can have different return types. This would be difficult to model, since now you have dependent sum types.
I agree that this would be a useful feature in some situations. For example, LosslessStringConvertible has a var description: String requirement, even though generally type conversions are done through initializers, not properties, and the description property is not intended to be used by clients. One could imagine an alternate version of the protocol that instead had a String.init(Self) requirement. But it would also have the downside of complicating the language, especially since other languages with protocols, interfaces, or inheritance typically restrict type-based dispatch to "type internal" declarations.
You've got my intention almost 100% correct, however I've asked about protocols and not "raw generics" for a reason - such requirements would still be needed to be explicitly wrapped in a conformance to a protocol - for the same reason that types that satisfy protocol requirements, still need to have explicit extensions to a protocol.
The reason I've asked this question is that because as far as I know all protocol requirements are "internal to the type" - I can only declare what static and member properties that type has, but not what I can do with the type (i.e convert to a Double or apply a trigonometric function to it). I was curious whether it is possible in the language in its current state.
In case it is not possible, it's fine - after all it can be expressed by wrapping the external function call in a method or doing something similar. It wouldn't even save too much typing - as defining the function on a new type, would require explicitly adding the protocol confirmation to the new type, which can still be done by adding a single line.