Generic type not preserved when called within another generic function - swift

[Updated with a less contrived example]
I'm trying to extend a generic function to provide different behavior for a specific type. This works as expected when I call the function directly. But If I call it from within another generic function, the original generic type is not preserved and I get the default behavior. I'm a bit new to Swift, so I may be missing something obvious here.
My code looks something like this:
protocol Task {
associatedtype Result
}
struct SpecificTask: Task {
typealias Result = String
// other taks related properties
}
enum TaskRunner<T: Task> {
static func run(task: T) throws -> T.Result {
// Task not supported
throw SomeError.error
}
}
extension TaskRunner where T == SpecificTask {
static func run(task: T) throws -> T.Result {
// execute a SpecificTask
return "Some Result"
}
}
func run<T: Task>(task: T) throws -> T.Result {
// Additional logic related to running the task
return try TaskRunner.run(task: task)
}
print(try TaskRunner.run(task: SpecificTask())) // Prints "Some Result"
print(try run(task: SpecificTask())) // Throws SomeError
I need the top-level run function to call the SpecificTask version of the lower-level run() function, but the generic version of the function is called instead

You're trying to reinvent class inheritance with generics. That is not what generics are for, and they don't work that way. Generic methods are statically dispatched, which means that the code is chosen at compile-time, not runtime. An overload should never change the behavior of the function (which is what you're trying to do here). Overrides in where clauses can be used to improve performance, but they cannot be used to create dynamic (runtime) dispatch.
If you must use inheritance, then you must use classes. That said, the problem you've described is better solved with a generic Task rather than a protocol. For example:
struct Task<Result> {
let execute: () throws -> Result
}
enum TaskRunner {
static func run<Result>(task: Task<Result>) throws -> Result {
try task.execute()
}
}
let specificTask = Task(execute: { "Some Result" })
print(try TaskRunner.run(task: specificTask)) // Prints "Some Result"
Notice how this eliminates the "task not supported" case. Rather than being a runtime error, it is now a compile-time error. You can no longer call this incorrectly, so you don't have to check for that case.
If you really want dynamic dispatch, it is possible, but you must implement it as dynamic dispatch, not overloads.
enum TaskRunner<T: Task> {
static func run(task: T) throws -> T.Result {
switch task {
case is SpecificTask:
// execute a SpecificTask
return "Some Result" as! T.Result // <=== This is very fragile
default:
throw SomeError.error
}
}
}
This is fragile because of the as! T.Result. If you change the result type of SpecificTask to something other than String, it'll crash. But the important point is the case is SpecificTask, which is determined at runtime (dynamic dispatch). If you need task, and I assume you do, you'd swap that with if let task = task as? SpecificTask.
Before going down that road, I'd reconsider the design and see how this will really be called. Since the Result type is generic, you can't call arbitrary Tasks in a loop (since all the return values have to match). So it makes me wonder what kind of code can actually call run.

Related

Swift Passing a Generic from a Function

I have the following function :
Amplify.API.query(request: .get(
self.getObjectForOCObject(ocObject: ocObject)!, byId: id)) { event in
The definition of the get function is :
public static func get<M: Model>(_ modelType: M.Type,
byId id: String) -> GraphQLRequest<M?> {
My getObjectForObject is as follows :
func getObjectForOCObject <M: Model>(ocObject:NSObject) -> M.Type?
{
if type (of:ocObject) == type(of: OCAccount.self)
{
return Account.self as? M.Type
}
return nil;
}
But I'm getting a"Generic Parameter 'M' could not be inferred. I get that the compiler cannot determine what type of object this is. However I don't know how to fix this. I need my getObjectForOCObject function to return what it is expecting. Essentially I'm trying to return the type of object based on the type of another object.
Normally this call is done this way and it works...
Amplify.API.query(request: .get(Account.self, byId: id)) { event in
I'm trying to avoid having to create a number of functions to handle each different Model in my code as that is being handled in higher up in the call hierarchy. Which is why I'm trying to do this. Otherwise I'm going to have to write essentially the same code in multiple places. In Objective C one can use the super class to pass objects around but the inheriting class still comes along with it. But with Swift being dynamically typed, I'm running into this issue.
The closest I've gotten is this... Changing the getObjectForOCObject return to Model.Type? as so :
func getObjectForOCObject (ocObject:NSObject) -> Model.Type?
{
if type (of:ocObject) == type(of: OCAccount.self)
{
return Account.self
}
return nil;
}
Then I get a Cannot convert value of type 'Model.Type?' to expected argument type 'M.Type' But aren't these essentially the same thing based on the .get function above?
Hoping for some insight here. Also, FYI, I'm pretty terrible with Swift so I'm certain I'm definitely doing some bad things in Swift here. I'm an expert with ObjC. I'm just trying to create a bridge between the two for this Swift library I'm using.

Extension with a generic where clause is not beeing called

The following code works just as expected:
protocol Storeable {
associatedtype Value
func create(value: Value)
}
struct DefaultStore<V>: Storeable {
typealias Value = V
func create(value: V) {
print("base impl. is called with value type: \(String(describing: V.self))")
}
}
class Event {
var title: String = ""
init(title: String) {
self.title = title
}
}
extension DefaultStore where Value: Event {
func create(value: V) {
print("event impl. is called with event: \(value.title)")
}
}
DefaultStore().create(value: Event(title: "Dance Party"))
// prints "event impl. is called with event: Dance Party"
DefaultStore().create(value: "a string object")
// prints "base impl. is called with value of type: String"
Basically I call a function on the generic class DefaultStore and the compiler calls the correct implementation depending on the underlying type.
Now I have a scenario where I want the exact same thing except, that it is wrapped in another object called Wrapper:
struct Wrapper<StoreType: Storeable,ValueType> where StoreType.Value == ValueType {
let store: StoreType
func create(value: ValueType) {
store.create(value: value)
}
}
let defaultStore = DefaultStore<Event>()
let wrapper = Wrapper(store: defaultStore)
wrapper.create(value: Event(title: "Düsseldorfer Symphoniker"))
// prints: "base impl. is called with value of type: Event"
Here I expect that the extension for Event is called but instead the base implementation is called. Anyone any idea what I am doing wrong?
--
Update:
#matt was guessing that there might be a compile time issue: "without the wrapper when you say DefaultStore().create... the Event info can be read all the way back to the DefaultStore generic parameterized type. But in your wrapper the DefaultStore is created first so it is the general type and that's all it is."
I believe that is not the case because you can also create the DefaultsStore first and it's still working without the Wrapper. Also the compiler warns me if the types don't match.
Nothing to do with "wrappers".
You seem to think that a where extension on a generic struct is a kind of substitute for some sort of dynamic dispatch, i.e. we're going to wait until runtime, look to see what kind of object the parameter to create really is, and dispatch accordingly. That's not how it works. Everything has to be resolved at compile time. At compile time, what we know is that you are not calling create with an Event — you are calling it with a ValueType. So it passes into the create with generic V.
So, for example:
struct Wrapper {
let store: DefaultStore<Event>
func create(value: Event) {
store.create(value: value)
}
}
That works the way you have in mind because we are exposing the fact that this is an Event as far as the DefaultStore is concerned at the point of calling create.
That's just a guess at what your confusion is.
Another possibility is you might imagine that the generic resolution of ValueType somehow "leaks" up to the resolution of V, but that's not true either.
Yet another possibility is you might suppose that a DefaultStore<Event> is a subtype of DefaultStore<Storable> or similar, as if there were dynamic dispatch for parameterized types, but that is exactly what is not the case. Everything has to be known completely at compile time at the call site.

What is the Never return type?

What does a func with return type Never do?
For example:
func addNums() -> Never {
//my code
}
What will be the difference if I kept the return type as Void like this?
func addNums() -> Void {
//my code
}
Suppose I wish to handle a fatalError (as said by dpassage); the below code will be sufficient:
print("its an error")
return
Apple documentation says:
The return type of functions that do not return normally, that is, a type with no values.
Source: Developer
This was not a duplicate question of When and how to use #noreturn attribute in Swift?, as I wish for a more detailed answer which needs details like:
Practical examples on the difference between both Never and Void as return types
Condition by which we should adopt these return types.
Also there is a chance the return type can be nil; I need a comparison of that feature too
The answer should focus on the differences.
Never return type was introduced in Swift 3 to substitute #noreturn key.
See justification in this proposal:
SE-0102 Remove #noreturn attribute and introduce an empty Never type
As official documentation explains:
The return type of functions that do not return normally; a type with
no values.
Use Never as the return type when declaring a closure,
function, or method that unconditionally throws an error, traps, or
otherwise does not terminate.
Source: https://developer.apple.com/documentation/swift/never
Basic illustration:
// The following function is our custom function we would use
// to manually and purposefully trigger crash. In the logs,
// we can specify what exactly went wrong: e.g. couldn't cast something,
// couldn't call something or some value doesn't exist:
func crashApp() -> Never {
fatalError("Something very, very bad happened! Crash the app!")
}
Usage specifics and advantages over #noreturn, as referenced by Erica Sadun:
Never allows a function or method to throw: e.g. () throws -> Never. Throwing allows a secondary path for error remediation, even in functions that were not expected to return.
As a first class type, Never works with generics in a way that the #noreturn attribute could not.
Never proactively prevents a function from claiming both a return type and no-return at the same time. This was a potential issue under the old system.
First note (regarding secondary error remediation) is probably particularly important. Never function can have complex logic and throw – not necessarily crash.
Let's see some interesting use cases and comparison between Never and Void
Never
Example 1
func noReturn() -> Never {
fatalError() // fatalError also returns Never, so no need to `return`
}
func pickPositiveNumber(below limit: Int) -> Int {
guard limit >= 1 else {
noReturn()
// No need to exit guarded scope after noReturn
}
return rand(limit)
}
Example 2
func foo() {
abort()
print("Should not reach here") // Warning for this line
}
Example 3
func bar() -> Int {
if true {
abort() // No warning and no compiler error, because abort() terminates it.
} else {
return 1
}
}
abort() is defined as:
public func abort() -> Never
Void
These examples would not have been possible with it returning Void:
public func abortVoid() -> Void {
fatalError()
}
func bar() -> Int {
if true {
abortVoid() // ERROR: Missing return in a function expected to return 'Int'
} else {
return 1
}
}
And to pack it up with abort() returning Never:
func bar() -> Int {
if true {
abort() // No ERROR, but compiler sees it returns Never and warns:
return 2 // Will never be executed
} else {
return 1
}
}
We use Void to tell compiler there is no return value. Application keeps running.
We use Never to tell compiler there is no return to caller site. Application runloop is terminated.
Void
Void is itself a return type which is a tuple with zero elements. You can use Void and () interchangeably.
Look at these examples,
func yourFunc() {} This is a function without a return type, which basically returns a tuple with zero elements, that can be written as ()
func yourFunc() -> Void {} Function which is explicitly informing the compiler about return type of void
func yourFunc() -> () {} This return type of () displays the same as void type. () indicates a tuple with zero elements
Never
Never return-type informs the compiler that no need exists to return an empty tuple (). Also, function with the never return type is used for the exit point of the current execution like a crash, fatal error, abort or exit.
For a detailed understanding of never, let's have a look at an abort() example :
1.
func yourFunc() {
abort()
print("Will not reach at this point") //Warning for this line
}
2.
func yourFunc() -> Int {
if true {
abort()
} else {
return 1
}
}
From the above code snippets, we can see when we call abort() (which doesn't return a value) as the last statement in a function that expects a value to be returned (in our case Int). The compiler doesn't generate a warning.
abort()
public func abort() -> Never
Similarly for exit():
public func exit(_: Int32) -> Never
The apple documentation says: "Use Never as the return type when declaring a closure, function, or method that unconditionally throws an error, traps, or otherwise does not terminate."
So if you want to write a custom function that logs a catastrophic error, you should use the return type Never to signal to the compiler:
func catastrophicErrorDisplay(error: String) -> Never {
DisplaySomeCustomLogFacility(error)
}
In short "Never is used for sudden and total failure from which recovery is impossible."
To better understand Never and Void, and how Never is useful in more contexts than the old #noreturn was, let's first look at what the two types actually are defined as:
Never is defined here as:
public enum Never {}
Since there is no way to instantiate a value of an empty enum, the type system guarantees that no instance of Never can exist. This means functions that specify their return type as Never are prevented by the type system from actually returning under any circumstances.
The compiler takes this into account when doing control-flow analysis. For example, these two functions both compile without error, whereas they would fail if a function that returns Void was substituted for fatalError:
func foo(fail: Bool) -> String {
if fail {
fatalError()
} else {
return "foo"
}
// notice there is no return statement here
}
func bar(fail: Bool) -> Void {
let s: String
if fail {
fatalError()
// the compiler doesn't complain s is not initialized here
} else {
s = "bar"
}
print(s)
}
Void is defined here as:
public typealias Void = ()
There are no two different instances of an empty tuple. Thus, the return value of functions returning Void holds no information.
You can actually write return () or return Void(). You can also use the "value" returned, like this:
func empty() -> Void {}
let v = empty()
print(type(of: v)) // prints "()"
although the compiler will warn "Constant 'v' inferred to have type 'Void', which may be unexpected".
Defining both Never and Void in terms of the type system rather than as special language features enables us to do some pretty clever things with generics. Let's look at an example of a Result type, generic over both the success and failure type.
enum Result<R, E> {
case success(R)
case failure(E)
}
A possible specialization of this would be Result<Void, MyError>. This would mean you have a result that, on success, does not hold any information beyond the fact it succeeded.
Another possibility could be Result<String, Never>. This result is guaranteed by the compiler to never be the failure case.
Optionals interact with Never and Void in a similar way. Never? can only ever be nil, and Void? only holds the information wether it is nil or not, nothing more (it's basically a more complicated Bool). Both of these are not very useful on their own, but might appear when Never or Void are used as generic parameters somewhere.
In practice, you will rarely write functions returning Never. I have personally used it to wrap fatalError to create a function I use to mark functions that are not implemented yet:
func unimplemented(f: String = #function) -> Never {
fatalError("\(f) is not implemented yet")
}
Another example of a function returning Never is dispatchMain(), which can be used in command-line utilities to start the DispatchQueue.main. Since this queue then waits for new blocks, dispatchMain() never returns.
Never indicates that the function will never return. It's intended to be used for things like fatalError which cause your program to crash intentionally, often after logging an error. You probably shouldn't use it unless you're doing something like making a handler for catastrophic errors in your application.
This is different from a function which just doesn't return a value, as in your second snippet. You could also write that as func addNums() -> Void.

How send generic value through the function? [duplicate]

I have code that follows the general design of:
protocol DispatchType {}
class DispatchType1: DispatchType {}
class DispatchType2: DispatchType {}
func doBar<D:DispatchType>(value:D) {
print("general function called")
}
func doBar(value:DispatchType1) {
print("DispatchType1 called")
}
func doBar(value:DispatchType2) {
print("DispatchType2 called")
}
where in reality DispatchType is actually a backend storage. The doBarfunctions are optimized methods that depend on the correct storage type. Everything works fine if I do:
let d1 = DispatchType1()
let d2 = DispatchType2()
doBar(value: d1) // "DispatchType1 called"
doBar(value: d2) // "DispatchType2 called"
However, if I make a function that calls doBar:
func test<D:DispatchType>(value:D) {
doBar(value: value)
}
and I try a similar calling pattern, I get:
test(value: d1) // "general function called"
test(value: d2) // "general function called"
This seems like something that Swift should be able to handle since it should be able to determine at compile time the type constraints. Just as a quick test, I also tried writing doBar as:
func doBar<D:DispatchType>(value:D) where D:DispatchType1 {
print("DispatchType1 called")
}
func doBar<D:DispatchType>(value:D) where D:DispatchType2 {
print("DispatchType2 called")
}
but get the same results.
Any ideas if this is correct Swift behavior, and if so, a good way to get around this behavior?
Edit 1: Example of why I was trying to avoid using protocols. Suppose I have the code (greatly simplified from my actual code):
protocol Storage {
// ...
}
class Tensor<S:Storage> {
// ...
}
For the Tensor class I have a base set of operations that can be performed on the Tensors. However, the operations themselves will change their behavior based on the storage. Currently I accomplish this with:
func dot<S:Storage>(_ lhs:Tensor<S>, _ rhs:Tensor<S>) -> Tensor<S> { ... }
While I can put these in the Tensor class and use extensions:
extension Tensor where S:CBlasStorage {
func dot(_ tensor:Tensor<S>) -> Tensor<S> {
// ...
}
}
this has a few side effects which I don't like:
I think dot(lhs, rhs) is preferable to lhs.dot(rhs). Convenience functions can be written to get around this, but that will create a huge explosion of code.
This will cause the Tensor class to become monolithic. I really prefer having it contain the minimal amount of code necessary and expand its functionality by auxiliary functions.
Related to (2), this means that anyone who wants to add new functionality will have to touch the base class, which I consider bad design.
Edit 2: One alternative is that things work expected if you use constraints for everything:
func test<D:DispatchType>(value:D) where D:DispatchType1 {
doBar(value: value)
}
func test<D:DispatchType>(value:D) where D:DispatchType2 {
doBar(value: value)
}
will cause the correct doBar to be called. This also isn't ideal, as it will cause a lot of extra code to be written, but at least lets me keep my current design.
Edit 3: I came across documentation showing the use of static keyword with generics. This helps at least with point (1):
class Tensor<S:Storage> {
// ...
static func cos(_ tensor:Tensor<S>) -> Tensor<S> {
// ...
}
}
allows you to write:
let result = Tensor.cos(value)
and it supports operator overloading:
let result = value1 + value2
it does have the added verbosity of required Tensor. This can made a little better with:
typealias T<S:Storage> = Tensor<S>
This is indeed correct behaviour as overload resolution takes place at compile time (it would be a pretty expensive operation to take place at runtime). Therefore from within test(value:), the only thing the compiler knows about value is that it's of some type that conforms to DispatchType – thus the only overload it can dispatch to is func doBar<D : DispatchType>(value: D).
Things would be different if generic functions were always specialised by the compiler, because then a specialised implementation of test(value:) would know the concrete type of value and thus be able to pick the appropriate overload. However, specialisation of generic functions is currently only an optimisation (as without inlining, it can add significant bloat to your code), so this doesn't change the observed behaviour.
One solution in order to allow for polymorphism is to leverage the protocol witness table (see this great WWDC talk on them) by adding doBar() as a protocol requirement, and implementing the specialised implementations of it in the respective classes that conform to the protocol, with the general implementation being a part of the protocol extension.
This will allow for the dynamic dispatch of doBar(), thus allowing it to be called from test(value:) and having the correct implementation called.
protocol DispatchType {
func doBar()
}
extension DispatchType {
func doBar() {
print("general function called")
}
}
class DispatchType1: DispatchType {
func doBar() {
print("DispatchType1 called")
}
}
class DispatchType2: DispatchType {
func doBar() {
print("DispatchType2 called")
}
}
func test<D : DispatchType>(value: D) {
value.doBar()
}
let d1 = DispatchType1()
let d2 = DispatchType2()
test(value: d1) // "DispatchType1 called"
test(value: d2) // "DispatchType2 called"

Is it possible to have an array of instances which take a generic parameter without knowing (or caring) what the parameter is?

Consider the following test case, which contains a 'factory' class which is able to call a closure it contains, providing a new instance of some 'defaultable' type:
protocol Defaultable {
init()
}
extension Int: Defaultable { }
extension Double: Defaultable { }
extension String: Defaultable { }
class Factory<T : Defaultable> {
let resultHandler: (T) -> ()
init(resultHandler: (T) -> ()) {
self.resultHandler = resultHandler
}
func callResultHandler() {
resultHandler(T.init())
}
}
Now, this works well when I use it on its own, where I can keep track of the generic type:
// Create Int factory variant...
let integerFactory = Factory(resultHandler: { (i: Int) in print("The default integer is \(i)") })
// Call factory variant...
integerFactory.callResultHandler()
Unfortunately, it doesn't work so well if I want to use factories in a way where I can't keep track of the generic type:
// Create a queue of factories of some unknown generic type...
var factoryQueue = [Factory]()
// Add factories to the queue...
factoryQueue.append(integerFactory)
factoryQueue.append(doubleFactory)
factoryQueue.append(stringFactory)
// Call the handler for each factory...
for factory in factoryQueue {
factory.callResultHandler()
}
I understand the error I get (Generic parameter 'T' could not be inferred), but I don't understand why I can't do this, because when I interact with the array, I don't need to know what the generic parameter is (I don't interact with any of the generic things in the Factory instance). Is there any way I can achieve the above?
Note that the above is a simplified example of what I'm trying to do; in actuality I'm designing a download manager where it can infer what type of file I want (JSON, image, etc.) using generics; the protocol actually contains an init(data:) throws initialiser instead. I want to be able to add the download objects to a queue, but I can't think of any way of adding them to a queue because of the generic nature of the download objects.
The problem is that Swift's strict type safety means you cannot mix two instances of the same class with different generic parameters. They are effectively seen as completely different types.
However in your case, all you're doing is passing a closure to a Factory instance that takes a T input, and then invoking it at any given time with T.init(). Therefore, you can create a closed system in order to contain the type of T, meaning that you don't actually need your generic parameter to be at the scope of your class. You can instead restrict it to just the scope of the initialiser.
You can do this by defining your resultHandler as a Void->Void closure, and create it by wrapping the passed closure in the initialiser with another closure – and then passing in T.init() into the closure provided (ensuring a new instance is created on each invocation).
Now whenever you call your resultHandler, it will create a new instance of the type you define in the closure that you pass in – and pass that instance to the closure.
This doesn't break Swift's type safety rules, as the result of T.init() is still known due to the explicit typing in the closure you pass. This new instance is then being passed into your closure that has a matching input type. Also, because you never pass the result of T.init() to the outside world, you never have to expose the type in your Factory class definition.
As your Factory class itself no longer has a generic parameter, you can mix different instances of it together freely.
For example:
class Factory {
let resultHandler: () -> ()
init<T:Defaultable>(resultHandler: (T) -> ()) {
self.resultHandler = {
resultHandler(T.init())
}
}
func callResultHandler() {
resultHandler()
}
}
// Create Int factory variant...
let integerFactory = Factory(resultHandler: { (i: Int) in debugPrint(i) })
// Create String factory variant...
let stringFactory = Factory(resultHandler: { (i: String) in debugPrint(i) })
// Create a queue of factories of some unknown generic type...
var factoryQueue = [Factory]()
// Add factories to the queue...
factoryQueue.append(integerFactory)
factoryQueue.append(stringFactory)
// Call the handler for each factory...
for factory in factoryQueue {
factory.callResultHandler()
}
// prints:
// 0
// ""
In order to adapt this to take an NSData input, you can simply modify the resultHandler closure & callResultHandler() function to take an NSData input. You then just have to modify the wrapped closure in your initialiser to use your init(data:) throws initialiser, and convert the result to an optional or do your own error handling to deal with the fact that it can throw.
For example:
class Factory {
let resultHandler: (NSData) -> ()
init<T:Defaultable>(resultHandler: (T?) -> ()) {
self.resultHandler = {data in
resultHandler(try? T.init(data:data)) // do custom error handling here if you wish
}
}
func callResultHandler(data:NSData) {
resultHandler(data)
}
}
I recently came back to needing a better answer for this question—as I was performing some refactoring—and thought that it would be really useful to have generic properties of the class, which of course would mean that the class itself would have to be generic as well.
I'm not sure why it didn't occur to me before, but I can simply create a protocol which mirrors the non-generic methods of the class. Using the example I originally had in my question, I could create a FactoryProtocol like so:
protocol FactoryProtocol {
func callResultHandler()
}
Make the class conform to it:
class Factory<T : Defaultable>: FactoryProtocol
And then use the protocol rather than the class when I define my array:
var factoryQueue = [FactoryProtocol]()
This allows me to add any type of specialised Factory to the array and interact with the non-generic methods as I please.
I am afraid this is not possible. The reason for this is that Swift doesn't have first class metatypes. I can imagine all sorts of Monads and Functors being built if this was possible. Unfortunately, this is a limitation. Welcome to Swift.
The golden rule is that in Swift, you cannot nail a type down to a protocol. Swift needs a concrete type.
Check this article out for more details around the subject.