Given the following function declaration
func foo(f:()->Foo) -> Bar
What is the difference in the following two variants of code using Closure Expressions:
A)
let result = foo {
return Foo()
}
B)
let result = foo {
Foo()
}
Please notice that the type of the constant result is not specified and must be inferred.
The reason why I am asking is that the compiler seems to make a difference - at least currently. This is due to the fact that in quite many scenarios the compiler is unable to infer the type of the closure expression when using return Foo() as the closure expression. Omitting the return on the other hand may issue another error by the compiler since it may require that return (even though I disagree with the compiler, but I do digress ...)
The issue can most often be solved by completely specifying the closure expression, e.g.:
let result = foo { () -> Foo in
return Foo()
}
or sometimes it can be alleviated by explicitly specifying the type of result.
foo function returns Bar. So compiler can easily infer that result is Bar. Foo() returns Foo instance. So compiler can easily infer that closure is correct whether you specify return or not. Here is the code that I play with in Xcode 6.0.1 playground.
struct Foo {
func foos() {
println("foos")
}
}
struct Bar {
func bars() {
println("bars")
}
}
func foo(f: () -> Foo) -> Bar {
let foof = f()
foof.foos()
return Bar()
}
let result = foo {
return Foo()
}
result.bars()
Related
this is my first question and I hope you guys can help me out.
Lets assume I've written a Framework in Swift that does stuff purely with native Swift Types. This is our interface:
class Foo {
#discardableResult
public func perform(query: String) -> [String: Any]? {
return [:]
}
}
Note that our result is discardable.
I would now like to support other Libraries optionally. I know we have Codable and stuff but lets assume that I want to support SwiftyJSON optionally by creating an extension that only compiles when SwiftyJSON can be imported.
#if canImport(SwiftyJSON)
import SwiftyJSON
extension Foo {
#discardableResult
func perform(query: String) -> JSON? {
// Call internal func => cast JSON => return JSON
return JSON()
}
}
#endif
Without SwiftyJSON (can not import) this compiles:
let bar = Foo().perform(query: "")
With SwiftyJSON it does not because its ambiguous.
Return type has to be explicitly defined like:
let baz: [String: Any]? = Foo().perform(query: "")
// or
let bar: JSON? = Foo().perform(query: "")
For calls where I want the result thats fine.
BUT: Since we have a discardableResult calls like
Foo().perform(query: "")
will always be ambiguous.
The following calls work but are not very nice imho.
Foo().perform(query: "") as JSON?
Foo().perform(query: "") as [String: Any]?
What is the best way to deal with this problem? Should the method just not be overloaded and have a different name instead? Or am I overlooking something that makes the calls non ambiguous in a nice way?
Edit:
A comment suggested to remove discardableResult. I really do not wanna do that since that would lead to a lot of calls that look like this:
_ = Foo().perform(query: "")
The more I think about the problem it occurs to me that there might just not be a good solution for this..
Actually, removing #discardableResult wouldn't work, and _ = Foo().perform(query: "") wouldn't compile, it would still have the same ambiguity: it's not that the compiler is being nitpicky, but it has literally no way of knowing which of your two functions to call!
It might be interesting to think about what behavior you'd expect from this line, and why.
You seem to want to have a function based on an existing function which:
Returns a different type;
Has the same name and parameters;
Has a discardable result.
Unfortunately, it looks like it's a classic "pick any two" type of scenario…
Well, let's do just that! Let's see what happens if we stop insisting on any one of the three conditions.
Let me use a simpler example:
#discardableResult func doSomething() -> String {
return "a"
}
#discardableResult func doSomething() -> Int {
return 1
}
doSomething() // won't compile
let x:String
x = doSomething() // "a"
We have a function overloaded with two return types (String and Int), and we see that we cannot discard its result as it would result in unresolvable ambiguity.
1. Single return type: use an enum with a payload
Let's eliminate the first condition, and try using a single return type. This is possible:
enum ReturnType {
case text(_ payload:String)
case number(_ payload:Int)
}
#discardableResult func doSomething() -> ReturnType {
if ... // some condition
{
return .text("a")
} else {
return .number(1)
}
}
doSomething() // works
let x = doSomething() // ReturnType.text("a") or ReturnType.number(1)
Here we cannot (easily) extend the functionality via extensions; rather, we'd need to touch the enum and function code with all the possible options every time we want to add a new return type. (Of course advanced APIs can also be created that help third parties easily write extensions… If it's worth the effort.)
We would also need a way to determine which return type to choose: it can be a function parameter, or a value stored elsewhere, etc. Here's an example with a parameter:
#discardableResult func doSomething(useText:Bool = false) -> ReturnType {
if useText {
return .text("a")
} else {
return .number(1)
}
}
doSomething() // works
doSomething(useText:true) // works
let x = doSomething() // ReturnType.number(1)
let x2 = doSomething(useText:false) // ReturnType.number(1)
let x3 = doSomething(useText:true) // ReturnType.text("a")
Note: Here, we lose the convenience of having the compiler infer the type from the call site; since the type is now an opaque wrapper, it's no longer the compiler's business to make sure that the wrapped type is correct. This may be a high cost for simply maintaining "result discardability," but then it may also allow us to abstract away some details, only unwrapping the "payload" when needed, which can have its own benefits.
2. Change the name or parameters of the function
This is quite easy:
#discardableResult func doSomething() -> String {
return "a"
}
#discardableResult func doSomethingElse() -> Int {
return 1
}
doSomething() // works
doSomethingElse() // works
let x = doSomething() // "a"
let y = doSomethingElse() // 1
We can also use this in an extension. All we lose is the shared name.
Changing the parameters is also possible but it would be pretty silly in this already-silly example:
#discardableResult func doSomething() -> String {
return "a"
}
#discardableResult func doSomething(thinkOfAnyNumber:Int) -> Int {
return 1
}
doSomething() // "a"
doSomething(thinkOfAnyNumber:42) // 1
Note: I'd obviously only do this if the additional parameters actually make sense.
Note 2: The parameter configuration is different between the two functions, not the parameter values like in the previous case.
3. Do not make the result discardable
Note that simply removing the #discardableResult attribute won't make it impossible to try to discard the result: you'll still be able to try the _ = assignment, or simply ignore the function result (and the compiler warning). Both will ultimately fail to compile, so it will be up to the user of the API to avoid doing either.
Hopefully, your function does some other things (side effects) than provide a return value: in that case, there may be little use in discarding the result of a function that does nothing but provide that result, and one may probably be better off not calling it in the first place.
If the side effects are identical between the two overloaded functions, you can factor them out into a single function:
func doTheActualWork() {
// ...
}
func doSomething() -> String {
doTheActualWork()
return "a"
}
func doSomething() -> Int {
doTheActualWork()
return 1
}
doSomething() // won't compile
doTheActualWork() // use this instead
let z:String = doSomething() // "a"
Note that this can also be done via extensions as long as they re-use existing functions and only overload them with different signatures and/or return types.
Now if the side effects (i.e. doTheActualWork() implementations) are also different in the two cases… Then I give up. But that can be handled as well.
Well, this is what I've been able to gather on this interesting problem. Now I may be dead wrong, and there may be something better out there… We'll find out soon enough.
I have met a problem like what title has described: the guard statement breaks type inference for some reason. I have created a Playground project to play around.
Here are some boilerplate code for setup:
import Foundation
struct Model: Decodable {
var i: String
}
let jsonString = """
{
"i": "yes"
}
"""
let jsonData = jsonString.data(using: .utf8)
let theModel = try JSONDecoder().decode(Model.self, from: jsonData!)
struct Response<T> {
var decodedData: T?
}
enum Result<Value> {
case success(Value)
case failure
}
struct Client {
static let shared = Client()
private init() {}
func execute<T: Decodable>(completion: (Response<T>) -> ()) {
let decodedData = try! JSONDecoder().decode(T.self, from: jsonData!)
completion(Response(decodedData: decodedData))
}
}
Here's the problem:
struct ServicesA {
func loadSomething(completion: (Result<Model>) -> ()) {
Client.shared.execute { result in // error: generic parameter 'T' could not be inferred
guard let decodedData = result.decodedData else { return }
completion(Result.success(decodedData))
}
}
}
struct ServicesB {
func loadSomething(completion: (Result<Model>) -> ()) {
Client.shared.execute { result in
completion(Result.success(result.decodedData!))
}
}
}
ServicesA breaks whereas ServicesB compiles.
As what you can see, the only difference is guard let decodedData = result.decodedData else { return }. It breaks the type inference so that the Client.shared.execute function complains that the T can't be inferred.
I wonder why would this happen and what's the most appropriate way to deal with this issue.
TLDR; single line closure compilation is different from multi line
Long answer: Let’s forget single line closures for sometime. When we write generic function accepting a generic as argument type and then call this function, compiler needs to know at time of invocation about what’s the type of function needed i.e. the type of its argument. Now consider this argument is a closure. Again compiler needs to know what’s the type of closure (function signature) at time of invocation. This info should be available in the signature of the closure (i.e. argument and return type), compiler doesn’t worry (and rightly so) about the body of the closure. So Service A is behaving perfectly as expected from compiler i.e. the closure signature doesn’t give any clue of its type.
Now comes in single line closure. Swift compiler has a built in type inference optimisation for single line closures only. When you write single line closure, compiler first kicks in its type inference and try to figure out about the closure including its return type etc from its only single line of body . This type inference kicks in for single line closures (with or without the context of generics).
This type inference is the reason that your service B works
So I’ll rephrase the question as “Why does the type inference works for single line closures?” because that’s an extra feature provided by Swift for single line closures only. As soon as you move to multi line closure (its not guard statement specific, same will happen if you put print(“hello world”) as well), this type inference is no more available. Though there may be other type inference checks available for multi line closures.
What you can do is just provide the Type info in closure signature
i.e (result: Response< Model >)
The compiler only infers closure return types when there is a single statement in the closure.
let x = {
return 1
}()
// x == Int(1)
let y = { // error: unable to infer complex closure return type; add explicit type to disambiguate
print("hi")
return 2
}()
https://forums.swift.org/t/problems-with-closure-type-inference/11859/2
You need to type cast it result with Result< Model > so can compiler know about T
struct ServicesA {
func loadSomething(completion:(Result<Model>) -> ()) {
Client.shared.execute { (result:Response<Model>) in
guard let data = result.decodedData else {return}
completion(Result.success(data))
}
}
}
Problem is you are referencing data variable but the compiler doesn't know what type is this (because it is a Generic). Try this:
struct ServicesA {
func loadSomething<T:Model>(completion:(Result<T>) -> ()) {
Client.shared.execute { result:Response<T> in
guard let decodedData = result.decodedData else { return }
completion(Result.success(decodedData))
}
}
}
Consider type Foo:
class Foo {
var isBaz: Bool {
return false
}
func bar() {
print("some boring print")
}
}
Now let's say I want to iterate through a collection of class instances and call some function on each of them:
let someFoos: [Foo] = [Foo(), Foo(), Foo()]
someFoos.forEach { $0.bar() }
This syntax is quite compact, but it feels a bit awkward. Also, it cannot be used everywhere. For example, in an if statement condition:
if someFoos.contains { $0.isBaz } {
// compiler error: statement cannot begin with a closure expression
}
if someFoos.contains($0.isBaz) {
// compiler error: anonymous closure argument not contained in a closure
}
if someFoos.contains({ $0.isBaz }) {
// this is correct, but requires extra pair of parentheses
}
Ideally, it would be nice to write something like
someFoos.forEach(Foo.bar)
but as of Swift 2.1 this is not a correct syntax. Such way of referencing the function would be similar to the following:
func bar2(foo: Foo) -> Void {
print("some boring print")
}
someFoos.forEach(bar2)
Is there a better way to reference instance function? How do you prefer to write such expressions?
There are two different problems here. The trailing closure syntax
can be used when calling a function and the last parameter is a closure,
so
let b1 = someFoos.contains({ $0.isBaz })
let b2 = someFoos.contains { $0.isBaz }
are fully equivalent. However, the trailing closure syntax can be problematic in the condition of an if-statement:
if someFoos.contains({ $0.isBaz }) { } // OK
if someFoos.contains { $0.isBaz } { } // Compiler error
if (someFoos.contains { $0.isBaz }) { } // OK, as noted by R Menke
We can only speculate why the second one does not work. It could be that the compiler
takes the first { as the start of the if-body. Perhaps this will
change in a future version of Swift but probably it is not worth
the effort.
The other problem is about curried functions.
someFoos.forEach(bar2)
compiles because bar2 has the type Foo -> Void, and that is exactly
what the forEach() method expects. Foo.bar, on the other hand,
is a curried function (see http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/) which takes the instance as the first
argument. It has the type Foo -> () -> (). So
Foo.bar(someFoo)
is a closure with type () -> (), and
Foo.bar(someFoo)()
calls the bar method on the someFoo instance.
(Note: The following is not meant as an actual recommendation,
but only as a demonstration about curried functions and fun
with closures!)
To pass Foo.bar directly as an argument to forEach() we need to
"swap" the order of the parameters. Haskell has a "flip" function for that purpose,
and it is also possible in Swift (see e.g. How to write a flip method in Swift?):
func flip<A, B, C>(f: A -> B ->C) -> B -> A ->C {
return { b in { a in f(a)(b) } }
}
Then flip(Foo.bar) has the type () -> Foo -> (), so
the void argument of the bar method can be applied
flip(Foo.bar)()
to get a Foo -> () closure, and
flip(Foo.bar)()(someFoo)
calls the bar method on the someFoo instance.
And now we can call
someFoos.forEach (flip(Foo.bar)())
without using a closure expression { .. } !!
If isBaz were a method instead of a property
func isBaz() -> Bool { return false }
then you
could do the same in the if-expression:
if someFoos.contains(flip(Foo.isBaz)()) {
// ...
}
Again, this is only meant as a demonstration. Also properties
are not curried functions, so this cannot be done with
your isBaz property.
The $0 syntax is there to help you create a shortcut, but if you don't like it you can use the more complete form:
someFoos.forEach { thisFoo in thisFoo.bar() }
here is my code
class Foo<T> {
}
class Main {
static func test() {
var foo: Foo<Any>
var bar = Foo<String>()
//Do all my stuff with foo necessitating String
foo = bar
}
}
When I try to assign foo = bar I get an error Cannot assign a value of type Foo<String> to a value of type Foo<Any>.
I don't understand why I got this error, since String conforms to Any.
If I do exactly the same thing but using Array, I don't have any error
static func test() {
var foo: Array<Any>
var bar = Array<String>()
//Do all my stuff with foo necessitating String
foo = bar
}
Does anyone know what's wrong with my code?
Thanks
Arrays and dictionaries are special types that have this kind of behavior built in. This does, however, not apply to custom generic types. The type Foo<Any> is not a supertype (or in this case superclass) of Foo<String> even though Any is a supertype of String. Therefore, you cannot assign variables of these types to each other.
Depending on your particular case the solution outlined in Swift Cast Generics Type might work for you. When Foo wraps a value of type T you can add a generic constructor that converts the value from a differently typed instance of Foo.
class Foo<T> {
var val: T
init(val: T) {
self.val = val
}
init<O>(other: Foo<O>) {
self.val = other.val as! T
}
}
class Main {
static func test() {
var foo: Foo<Any>
var bar = Foo<String>(val: "abc")
//Do all my stuff with foo necessitating String
foo = Foo<Any>(other: bar)
}
}
I have improved on #hennes answer by adding the ability to define how you want to convert from type O to type T
class Foo<T> {
var val: T
init(val: T) {
self.val = val
}
init<O>(other:Foo<O>, conversion:(O) -> T) {
self.val = conversion(other.val)
}
}
class Main {
static func test() {
var foo: Foo<Int>
var bar = Foo<String>(val: "10")
//Do all my stuff with foo necessitating String
foo = Foo<Int>(other: bar, conversion: {(val) in
return Int(val.toInt()!)
})
print(foo.val) //prints 10
print(foo.val.dynamicType) //prints Swift.Int
}
}
This gives you the ability to convert between two types that don't support casting to each other. Additionally, it gives a compiler warning when the conversion is illegal, as opposed to crashing due to the forced cast.
No idea why there is a difference in compiler warnings for 1 or >1 line closures but there are.
Bad compiler warning for 1-line closure
Good compiler warning for 2-line closure
Due to Swift's lack of covariance, I needed some workaround. I'm coming from Java world, so I instinctively tried to create constraint from one type to other generic type.
So I wrote the following class:
class Factory<T: AnyObject> {
let factoryClosure: () -> T
init(closure: () -> T) {
factoryClosure = closure
}
init<CHILD: T>(childFactory: Factory<CHILD>) {
factoryClosure = { () -> T in
return otherFactory.create()
}
}
func create() -> T {
return factoryClosure()
}
}
I expected this to work just fine. I have the T defined and CHILD should be a subclass of T. Swift compiler however disagrees and shows the following error on the line with init<CHILD: T>.
Inheritance from non-protocol, non-class type 'T'
I tried the generic parameter inheritance in different scenario as well. Adding the following method into the class (and removing the init that was causing the compile error).
func to<OTHER where OTHER: AnyObject, T: OTHER>() {
}
This yields basically the same output.
Type 'T' constrained to non-protocol type 'OTHER'
Anything I though may work did not and ended with similar error message. Is this a bug in Swift? Or am I missing something? Or is it a feature of Swift and will never work as I thought?
If you want to pass any Factory<T> where T is of type AnyObject you just have to write:
init(childFactory: Factory<T>) {
factoryClosure = { () -> T in
return otherFactory.create()
}
}
because T is automatically constrained by your class.