@miku1958 Glad you can get it to work, but perhaps it helps you in the future to see that it is not a bug, but unavoidable, actually.
I'll try to explain it a bit further, but that means I am going to make assumptions about the code outside of what you posted here (side note: totally understandable and good practice to not clutter everything with what you had and instead providing a minimal example illustrating the issue).
@Jumhyn while it could be interesting to make the "sugared" syntax even smarter at inferring the parameters, it probably won't ever resolve the issue @miku1958 encountered here, as what they seem to have written kind of "explicitly" results in a conflict.
Handlers(
contentChanged: { // 1
#if os(macOS)
contentChanged($0) // 2a
#elseif os(iOS)
contentChanged($0, $1, $2) //2b
#endif
},
)
1: Before the {
is the label "contentChanged". That comes from the (probably) synthesized init
of the Handlers
struct. After the {
begins the closure. The closure's type is also inferred from the Handlers
's init (or rather, its property contentChanged
). It has (Bool) -> Void
type on macOS and (String, String, Bool) -> Void
on iOS. Let's call this closure "outer thunk" from now on for clarity.
2a: This "contentChanged" identifier has nothing to do with Handlers
's properties or init
. From what I can see in the example given I must assume it is a variable that comes from the calling context of the initializer! It just happens to also be named "contentChanged", but it could be named differently. What type this closure has is not clear in the example. Just because it is called in an #if os(macOS)
block that does not mean it has the same type as the similarly named property of Handlers
! I am not sure which part of the shown error message refers to here or maybe the "outer thunk" closure as this init call is broken over several lines and I can simply not see enough in the screenshot. Regardless, as written in the example above the screenshot, here, on macOS, this closure variable is called with the first and only parameter of the "outer thunk" closure that is defined inline (beginning with the {
).
2b: Similarly, this "contentChange" also has nothing to do with the Handlers
type itself, but apparently is defined outside this context. On iOS it is called with three parameters that come from the "outer thunk" closure defined inline.
My hunch is that the variable contentChanged
is defined without any compiler directives and is supposed to match the type of the Handlers
property contentChange
(once more: it's two different things with the same name! They live in different scopes!). Since it compiles on iOS I deduce it is defined as having the type (String, String, Bool) -> Void
, regardless of platform, but @miku1958's intention is to have it defined differently on each platform.
In this case one does not need to have an "outer thunk" in the first place and can pass the variable directly to the parameter labeled contentChanged
(I'd suggest to use a different name for the variable to make that clear). But you have to ensure the types match, by adding a compiler directive wherever the variable comes from. That's what I tried to relay with my earlier example. If it is a local variable defined right above the call to Handlers
's init, that definition needs to be packed into an #if
block accordingly, but other scenarios are possible.
Putting compiler directives inside "outer thunk" does not help at all, because that can only influence how it is called, but not which platform version.
This is not a bug, nor does it matter how smart the compiler can be at determining how many parameters there are for the $x
syntax sugar. Once you start to define different type signatures for different platform, you have to "complete" the differences to make the calls match. Down the line, the logic is different, after all, right? iOS uses three parameters to do something, whereas macOS only uses one.
The only thing that might be improved on in this case, I guess, is the error messages or warnings, but I wouldn't know how and I think to judge that we need to see how that variable is defined. However, I think once you start including compiler directives in your type definitions these scenarios are unavoidable, as you're fundamentally introducing in effect two distinct implementations within a single file. Everything that then uses this has to respect it and make the same distinctions.