New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SR-10065] Substring.count
is ambiguous inside a [Substring].filter
block
#52467
Comments
Comment by Michael Verges (JIRA) I think the compiler is taking too long implicitly evaluating the types within a long expression. Explicitly stating the type of substr within the closure is a workaround: "abc def ghi jklmno"
.split(separator: " ")
.filter({ (substr: Substring) -> Bool in
return substr.count == 3 })
.joined(separator: ", ") |
Comment by Michal Zelinka (JIRA) Yeah, that's possibly works around a bit more cleanly. 🙂 Still seems to be a serious issue — by omitting the "abc def ghi jklmno"
.split(separator: " ")
.filter({ (substr) -> Bool in
return substr.count == 3 })
// Outputs [["a", "b", "c"], ["d", "e", "f"], ["g", "h", "i"]] instead of ["abc","def","ghi"] Thus I might have no idea something wrong happened. That's neither predictable nor easily detectable unless investigating really carefully. Turning the The other thing is — should we consider this as a “long” expression, if thinking about it at all? Ruby can handle stuff like this in runtime flawlessly. |
Comment by Michal Zelinka (JIRA) I guess there's even more troubling stuff: ![](Screenshot 2019-03-09 at 22.13.14.png)
This is madness. There's something terribly wrong with string processing in Swift being a victim of horrible protocol and generics abstractions of {{Collection}}s getting this mess as a result. |
Comment by Michael Verges (JIRA) Looking through the docs, I found conflicting definitions of split, but you beat me to it in the attached screenshot. This explains why the type of substr in filter is ambiguous. Swift can not implicitly determine the return type of split, therefore the type of substr is ambiguous. I can not, however, understand why it chooses the [ArraySlice<Character>] in this case: "abc def ghi jklmno"
.split(separator: " ")
.filter({ (substr) -> Bool in
return substr.count == 3 })
//[["a", "b", "c"], ["d", "e", "f"], ["g", "h", "i"]] And [Substring] in this case: var a = "abc def ghi jklmno"
.split(separator: " ")
a.filter({ (substr) -> Bool in
return substr.count == 3 })
//["abc", "def", "ghi"] |
Comment by Michal Zelinka (JIRA) I guess the language design shouldn't allow this kind of ambiguity to even happen. One method definition cannot put out two different results not even in theory, the worse it what happens – it somehow-mostly works, but unpredictibly depending on the actual code. This bare code: "abc def ghi jklmno"
.split(separator: " ") Should be currently ambigous in any cases and revoke calling This code, adding "abc def ghi jklmno"
.split(separator: " ")
.filter({ (substr) -> Bool in
return substr.count == 3 }) Works as well. Once again, first mistake – no ambiguity resulting in compilation error, second mistake – filtering an array of {{Substring}}s turns into an array of {{ArraySlice<Character>}}s – switching to the second return type. Finally, this code, adding "abc def ghi jklmno"
.split(separator: " ")
.filter({ (substr) -> Bool in
return substr.count == 3 })
.joined(separator: ", ") At least fails to compile, even though with a useless error, but I can understand it as it's broken waaay way more beyond this scope. |
Comment by Michael Verges (JIRA) I agree, this ambiguity should not be allowed. The behavior should be to diagnose an error: "Ambiguous use of 'split'" This excerpt in playgrounds complains over ambiguity: func foo(_ a: Character, _ b: Int = 0) -> Int { return b }
func foo(_ a: Character, _ b: Double = 1.0) -> Double { return b }
var c: Character = " "
var i: Int = foo(c) // Explicit return type allowed
var d: Double = foo(c) // Explicit return type allowed
var a = foo(c) // Ambiguous return type
// ERROR: Ambiguous use of 'foo' For some reason, 'split' is not following this behavior. I think that is the core BUG here. |
Comment by Michal Zelinka (JIRA) Ambiguous (~duplicate) method definitions (= illegal redeclarations in any meaningful language) shouldn't be allowed at all, no matter if I declare the desired type or not. It can only lead to A) weird and unpredictable behaviour and B) a need for explicit type declaration to work around. Both of something I thought Swift should've eliminated. |
Comment by Michael Verges (JIRA) This is not a redeclaration, as the return type is different. The method signature can conflict, so it is ambiguous. Disallowing duplicate signatures altogether is beyond the scope of this bug. On another note, here is a breakdown of what is happening: "abc def ghi jklmno".split(separator: " ")
// can return [Substring] or [ArraySlice<Character>]
.filter({ (substr) -> Bool in
/*
'substr' is Substring or ArraySlice<Character>.
Therefore, type can not be determined.
Explicitly noting 'substr: Substring' or
'substr: ArraySlice<Character>'will allow the compiler
to implicitly determine the return type of 'split'.
*/
// substr is Substring or ArraySlice<Character>
return substr.count == 3
/*
Accesing a method or property that is exclusive to
Substring or ArraySlice<Character> will allow the compiler
to implicitly determine the return type of 'split'.
However, both Substring and ArraySlice<Character> have
property 'count'. Therefore, type can not be determined.
This is the last oportuniy for Swift to determine the
return type of 'split', so the error is shown that
'count' is ambiguous.
*/
}).joined(separator: ", ") The error makes sense and is expected, but it can be more explicit that 'split' is the culprit, not 'count'. Also, inserting a fixit for explicitly declaring the type. E.g. something like: |
Comment by Michal Zelinka (JIRA) Yeah, I get it. 🙂 But in case the discussion over some design flaw leads to higher level topic, it could possible pull some strings to reconsider. Allowing conflicting signatures only leads to situations nobody really wants to deal with/expects/can rely on. In extreme cases, f.e. 3rd-party extensions may duplicate pretty much any possible method from the stdlib, forcing the user to re-type code that's otherwise clear and straight-forward, or alternatively, chaging its behaviour without user's knowledge – that might even come up as a security flaw aspect ('cause nobody really walks through all the 3rd-party code). Other way, like in this case, a method to explode the string into |
Comment by Michael Verges (JIRA) The split method that returns [ArraySlice<Element>] is actually a generic function that String inherits from Collection protocol. Keep in mind that renaming this method affects all arrays, not only Strings. And in a lot of cases, the name ‘split’ makes a lot of sense for arrays. A more interesting approach would be to make ArraySlice<Element> implicitly convertible to Substring, and get rid of Strings split method. |
Comment by Michael Verges (JIRA) Do you think this discussion should be moved to the forums? |
Comment by Michal Zelinka (JIRA) Might be, but this topic would be a bit too late to perform some deeper updates, having the ABI stability in mind, nobody's really into doing some drastic changes. Which is pretty sad, many things done wrong at this moment may stick with us forever, like many nasty things stick with us in C/C++ standards. I get the inheritance points, but here we hit that aspiration to solve all issues generically. The other thing is – a method returning Stating Swift as an easy-to-learn language becomes difficult when we start to be limited by too much liberty, related ambiguity, need to specify something otherwise redundant if the rules were more strict and obvious. |
Comment by Michal Zelinka (JIRA) Would these proposals be sensible?
I think this is pretty mandatory and needed for easy strings manipulation & processing in terms of actual splitting. I guess all possible |
Comment by Michael Verges (JIRA) [1] What advantage would CollectionSlice have over ArraySlice? Seems extraneous to reinvent the wheel Also, if this really does uproot a lot of code, it could be difficult to approve a PR without a discussion. |
Comment by Michal Zelinka (JIRA) Ad 1+2 – no idea about slice types, I tried to describe it theoretically. As of now, Ad 3 – how's it possible right now? 🙂 I can't really find an easy way to split the string using another string. Very odd. 😃 |
Comment by Michal Zelinka (JIRA) Further discussion on forums: |
Attachment: Download
Environment
Swift 5 (swiftlang-1001.0.69)
Xcode 10.2 Beta 4 (10P107d)
Additional Detail from JIRA
md5: 11953ae492961923b0aff652c736f8f1
Issue Description:
I've created a simple Playground code demonstrating the issue.
Having a string, for example:
"abc def ghi jklmno"
When attempting to call a split–filter–join chain, like:
Swift 5 fails to compile the code, stating use of
.count
as ambiguous.Weird behaviours:
• When omitting the
.joined
operation, the compilation succeeds with no ambiguity aboutsubstr.count
call, but.filter
block incorrectly outputs a result of[[Character]]
type ([["a","b","c"], ["d"…]
) instead of correct[Substring]
type (["abc","def",…]
).• When using an intermediate variable after
.split
(thus breaking into two parts —let a = "…".split(…); let b = a.filter(…).joined(…)
), compilation succeeds with a correct output.• When touching the
substr
block variable before callingsubstr.count
(even by putting it on a separate line to only be printed in the variable pane in the Playgrounds), the compilation succeeds with a correct output — thus acts as a work-around.Steps to Reproduce:
See the attached Playground code.
Version/Build:
Xcode 10.2 Beta 4 (10P107d)
Swift version 5.0 (swiftlang-1001.0.69 clang-1001.0.45.2)
rdar://problem/48722421
https://openradar.appspot.com/48722421
The text was updated successfully, but these errors were encountered: