masters3d
(Chéyo Jiménez)
21
Seems strange. My mental model for never is it never returns. Void seems better here.
toph42
(Topher Hickman)
22
Isnât that because a bare return statement or exiting the function scope without a return actually does return an empty tuple (AKA Void)?
1 Like
As far as I can tell, a function that exits every of its paths with a throw has no implicit return.
There are two options from where I see the return type can be provided from:
- explicit but unreachable return
- explicit type for the whole closure which cancels the whole type inference
None of there two options are present in the edge case we're discussing.
My whole point is, that the current inference seems to provide a wrong return type in this case. It does feel like a bug. However it would be a bug that probably would need to go through evolution, hence this thread.
If the core team joins the conversation and tells that there is nothing we can / should do about it, so be it.
4 Likes
hborla
(Holly Borla)
24
Today, return type inference for multi-statement closures does not look at code in the closure body at all. If the closure does not have a type annotation or other type context (e.g. the closure is an argument in a function call where the parameter type provides context), the return type is simply defaulted to Void.
4 Likes
toph42
(Topher Hickman)
25
Iâm not sure I understand. Youâre saying that a closure that returns something else (say an Int for example) is treated as if its return type were Void if it doesnât have any type annotations?
toph42
(Topher Hickman)
26
I tried this in Playgrounds and it wouldnât compile, complaining that it could not determine the type and wanted an explicit annotation. I donât have access to XCode at the moment to see what it says, but how would one create the closure with no annotations and an inferred return type that @hborla mentions?
let closure = {
let value = 42
return value
} //Error: Unable to infer complex closure return type;
// add explicit type to disambiguate
hborla
(Holly Borla)
27
Hereâs an example:
let closure = {
let a = 1
print(a)
}
closure()
Thereâs no type annotation, so type inference defaults the return type to Void. It doesnât compile if you return a different type of value, because without type context, the only return type inference supported is the default Void type. Like I mentioned upthread, return type inference from statements in a multi-statement closure body is a work in progress.
7 Likes
toph42
(Topher Hickman)
28
Okay, I think I understand now. The only instance of a closure that has a different return type than itâs actual return type should be whatâs actually the subject of this Pitch: A multiline closure where all paths never return.
I see that type(of:) returns () -> Never for this closure
{
fatalError()
}
yet it returns () -> () AKA () -> Void when you add an irrelevant line such as in this closure
{
let _ = 0
fatalError()
}
so I think this illustrates both what youâre explaining to me and the issue that precipitated this Pitch.
This is because of Swiftâs implicit returns from single-expression closures.
{
fatalError()
}
turns into
{
return fatalError()
}
so it assumes a return type of () -> Never because of fatalError()âs return type of Never.
On the other hand,
{
let _ = 0
fatalError()
}
isnât a single-expression closure, so the compiler assumes the default return type of () -> () (a.k.a. () -> Void).
1 Like
Even if Never becomes the bottom type, this still would not compile.
let integer = instantiateAndMakeInt(from: Never.self)
This line uses the metatype Never.Type (Never.self) which is not a bottom type. Secondly, making Never the bottom type does not mean that it implicitly conforms to all protocols. These points are spelled out in the original pitch:
I see â I was under the impression that Never as a bottom type meant that it would conform to all protocols.
(Side note: the metatype isnât actually used in the functionâs code â just in its parameters. The metatype is used by the function to specify the I type parameter. Since I would resolve to Never and the function instantiates I, it would be instantiating a value to type Never â at least, if Never conformed to InstantiableAndConvertableToInt. This pattern is used elsewhere in Swift (like here and here).)
I do think Never as the bottom type would actually be helpful (though I think it should be applied to all uninhabited types if possible).
I disagree. Owen addressed this as well, and I agree with his argument: