Swift 4.1.2 Autocomplete: Suggesting extensions on Array that it shouldn't - arrays

Maybe I'm going crazy here, but extension Array where Element == String in Swift 4.1.2 is exposing its properties in Swift's autocomplete on types when it should not be there.
For example, this extension:
extension Array where Element == String {
public var test: [String] {
return ["test"]
}
}
Then start typing:
[123].te ...
and Swift suggests that the test property is also available on [Int] which is impossible. Then the syntax checker pops up the error:
Type of expression is ambiguous without more context
Is there something I'm missing? Perhaps some other/additional conformance restrictions that need to be used? Or is this a Swift bug?

It appears to be a bug.
This Swift.org bug report demonstrates the same behavior:
https://bugs.swift.org/browse/SR-5388

Related

Kotlin: Type inference failed: Not enough information to infer parameter E in fun <E> <init>(): kotlin.collections.ArrayList<E>

I declared a variable like this:
var G: Array<MutableList<Int>> = Array(0) { ArrayList() }
Kotlin gives me the following error:
Kotlin: Type inference failed: Not enough information to infer parameter E in fun <E> <init>(): kotlin.collections.ArrayList<E> /* = java.util.ArrayList<E> */
Please specify it explicitly.
It means Kotlin can't infer the type for the ArrayList which should be Int. So I add Int explicitly for the ArrayList like following:
var G: Array<MutableList<Int>> = Array(0) { ArrayList<Int>() }
Kotlin says - Remove explicit types arguments
In this case, Kotlin is ambivalent about how to act.
So is it possible to write code without explicitly declaring the type of ArrayList?
As discussed here,
The way it works curretly is that whenver we encounter a collection in Kotlin, we load a Kotlin version of this class (e.g. kotlin.Collection) instead of a Java version (java.util.*). Using the type java.util.Collection leads to a warning from the Kotlin compiler, because Kotlin's type checker is designed to distinguish between read-only and mutable collections.
So you can try to use like this,
var G = arrayOf<MutableList<Int>>()
Moreover, here are some a good stuff to know for you.
Kotlin says - Remove explicit types arguments
Kotlin doesn't (you can see there's no warning in https://pl.kotl.in/7v1h5Yobu). It's probably the IDEA plugin which does. If you look at https://youtrack.jetbrains.com/issues/KT?q=Remove%20explicit%20types%20arguments, you can see there are quite a few false positives. It may be worth checking if yours is actually one of them and posting a new issue if it isn't.
var G = Array<MutableList<Int>>(0) { ArrayList() }
should work without warning from IDEA either.

How to serialize/unserialize an Array of Custom object in Kotlin?

In my Kotlin Android project, I made a FileItem class which extends Serializable
class FileItem(<parameters>) : Serializable, Comparable<FileItem> {
So I needed to Serialize instances of this class into a Bundle
val arguments:Bundle = Bundle()
arguments.putSerializable("folders", folders as Serializable)
where folders has been declared as :
folders:Array<FileItem> (method parameter)
The serialization code above compile without any warning. Meanwhile, the problem comes when I need to unserialize folders items :
val arguments: Bundle? = getArguments()
if (arguments != null){
foldersItems = arguments.getSerializable("folders") as Array<FileItem>
where foldersItems is declared as
var foldersItems: Array<FileItem>?
I get the following warning, that I can't manage to solve without suppress_warning annotation :
w: <Path to my class>: (78, 28): Unchecked cast: java.io.Serializable! to kotlin.Array<com.loloof64.android.chess_positions_archiver.main_file_explorer.FileItem>
This kind of code compiles in Java/Groovy without warning (folderItems is then a FileItem[]), so how can I modify the kotlin code for the compiler to be "satisfied" ?
I noticed in official Kotlin documentation that Kotlin Array does not extend Serializable and is not open for inheritance. Is it possible meanwhite to "add" it via a kind of extension method ?
In fact, the cast is not unchecked, the compiler's warning is misleading.
This happens because in Kotlin arrays are represented by generic class Array<T>, and the compiler treats it as usual generic class with type parameters erased at runtime.
But on JVM arrays have reified types, and when you cast something as Array<SomeType>, the generated bytecode really checks the type parameter to be SomeType as well as something being an Array<*>, which would only happen for any other generic class.
This example shows that the array cast is checked:
val a: Any = Array<Int>(1) { 0 }
val i = a as Array<Int>
val d = a as Array<Double> // gets checked and throws ClassCastException
The easiest solution is indeed to #Suppress("UNCHECKED_CAST"), because actually there should not be any warning.
I filed an issue describing the problem in Kotlin issue tracker.
The cast here is unchecked because the compiler here can't ensure the nullability of array's generic type parameter.
Consider the following example:
fun castAsArrayOfString(param: Any) = param as Array<String>
castAsArrayOfString(arrayOf("a")) // is Array<String>, all ok
castAsArrayOfString(arrayOf("a", null)) // is Array<String>, but contains null
So the compiler warns you about potential type safety problems this cast could introduce.

Teach a (specialized) Swift Array to be Equatable [duplicate]

I would like extend Array to add conformance to a new protocol — but only for arrays whose elements themselves conform to a specific protocol.
More generally, I’d like to have types (whether protocols or concrete types) with type parameters implement a protocol only when the type parameters match certain constraints.
As of Swift 2.0, this appears to be impossible. Is there a way I’m missing?
Example
Suppose we have the Friendly protocol:
protocol Friendly {
func sayHi()
}
We can extend existing types to implement it:
extension String: Friendly {
func sayHi() {
print("Greetings from \(self)!")
}
}
"Sally".sayHi()
We can also extend Array to implement sayHi() when its elements are all Friendly:
extension Array where Element: Friendly {
func sayHi() {
for elem in self {
elem.sayHi()
}
}
}
["Sally", "Fred"].sayHi()
At this point, the type [Friendly] should itself implement Friendly, since it meets the protocol’s requirements. However, this code doesn’t compile:
extension Array: Friendly where Element: Friendly {
func sayHi() {
for elem in self {
elem.sayHi()
}
}
}
The error message is “extension of type 'Array' with constraints cannot have an inheritance clause,” which seems to shut the door definitively on that direct approach.
Is there an indirect workaround? Some clever trick I can use? Perhaps there’s a way that involves extending SequenceType instead of Array?
A working solution would make this code compile:
let friendly: Friendly = ["Foo", "Bar"]
Update: This has landed in Swift 4.1, and it is a thing of beauty!
The extension Array: Friendly where Element: Friendly example now compiles as given in the original question.
EDIT: As noted in the updated question, this is now possible since Swift 4.1
This is not currently possible in Swift (as of Xcode 7.1). As the error indicates, you can't restrict protocol conformance ("inheritance clause") to a type-constrained extension. Maybe someday. I don't believe there's any deep reason for this to be impossible, but it's currently not implemented.
The closest you can get is to create a wrapper type such as:
struct FriendlyArray<Element: Friendly>: Friendly {
let array: [Element]
init(_ array: [Element]) {
self.array = array
}
func sayHi() {
for elem in array {
elem.sayHi()
}
}
}
let friendly: Friendly = FriendlyArray(["Foo", "Bar"])
(You would likely want to extend FriendlyArray to be a CollectionType.)
For a tale of my own descent into the madness of trying to make this work, and my crawl back from the edge, see NSData, My Old Friend.
The good news is that what you are asking for Conditional Conformance is coming in Swift 4.1:
https://swift.org/blog/conditional-conformance/

Extending Array in Typescript breaks constructor

while playing around with typescript I ran into then following interesting behavior:
class ExtArray<U> extends Array<U> {
constructor(...args : U[]) {
super(...args);
}
public contains(element : U) : boolean {
var i = this.indexOf(element);
return i !== -1;
}
}
var test : ExtArray<string> = new ExtArray("a", "b", "c");
test.push("y");
console.log(test.length); // 1
console.log(test[0]); // y
console.log(test[1]); // undefined
console.log("Has a: " + test.contains("a")); // Has a: false
console.log("Has y: " + test.contains("y")); // Has y : true
I've added the output of the console.log statements as comments.
See this typescript playground for an executable example and the javascript code.
As you can see it seems as if the elements passed to the constructor are not added to the array.
The section about extending expression in Whats new in Typescript suggests that it should be possible to extend the native Array type like that in typescript 1.6.
Also I didn't find anything in the typescript language reference,
that explains this behavior.
Most of the other questions about extending Arrays I found here are at least one year old and usually talk about a pre-1.0 version of typescript and therefore suggest to set up the prototype chain directly.
I seriously don't see what is going wrong here and I'm starting to suspect a typescript bug.
Or at least some kind of undocumented restriction for extending Arrays.
What goes wrong here?
It's a little easier to understand what's going on if you JSON.stringify() your object:
var test : ExtArray<string> = new ExtArray("a", "b", "c");
test.push("y");
// outputs {"0":"y","length":1}
document.writeln(JSON.stringify(test));
If you instead new-up a native Array, the resulting object is quite a bit different:
var test : Array<string> = new Array("a", "b", "c");
test.push("y");
// outputs ["a","b","c","y"]
document.writeln(JSON.stringify(test));
I agree with you that the documentation seems to imply that the subclass's constructor should behave the way you're expecting. Even stranger, I seem to get inconsistent results when testing whether or not the subclass is an Array using the methods described here:
test.constructor === Array // false
test instanceof Array // true
Array.isArray(test) // false
I would suggest opening an issue on the TypeScript GitHub repository. Even if this is the expected behavior, the official documentation is misleading and should clarify what exactly is expected when native objects are subclassed.

How to both constrain an Array and signal protocol implementation [duplicate]

I would like extend Array to add conformance to a new protocol — but only for arrays whose elements themselves conform to a specific protocol.
More generally, I’d like to have types (whether protocols or concrete types) with type parameters implement a protocol only when the type parameters match certain constraints.
As of Swift 2.0, this appears to be impossible. Is there a way I’m missing?
Example
Suppose we have the Friendly protocol:
protocol Friendly {
func sayHi()
}
We can extend existing types to implement it:
extension String: Friendly {
func sayHi() {
print("Greetings from \(self)!")
}
}
"Sally".sayHi()
We can also extend Array to implement sayHi() when its elements are all Friendly:
extension Array where Element: Friendly {
func sayHi() {
for elem in self {
elem.sayHi()
}
}
}
["Sally", "Fred"].sayHi()
At this point, the type [Friendly] should itself implement Friendly, since it meets the protocol’s requirements. However, this code doesn’t compile:
extension Array: Friendly where Element: Friendly {
func sayHi() {
for elem in self {
elem.sayHi()
}
}
}
The error message is “extension of type 'Array' with constraints cannot have an inheritance clause,” which seems to shut the door definitively on that direct approach.
Is there an indirect workaround? Some clever trick I can use? Perhaps there’s a way that involves extending SequenceType instead of Array?
A working solution would make this code compile:
let friendly: Friendly = ["Foo", "Bar"]
Update: This has landed in Swift 4.1, and it is a thing of beauty!
The extension Array: Friendly where Element: Friendly example now compiles as given in the original question.
EDIT: As noted in the updated question, this is now possible since Swift 4.1
This is not currently possible in Swift (as of Xcode 7.1). As the error indicates, you can't restrict protocol conformance ("inheritance clause") to a type-constrained extension. Maybe someday. I don't believe there's any deep reason for this to be impossible, but it's currently not implemented.
The closest you can get is to create a wrapper type such as:
struct FriendlyArray<Element: Friendly>: Friendly {
let array: [Element]
init(_ array: [Element]) {
self.array = array
}
func sayHi() {
for elem in array {
elem.sayHi()
}
}
}
let friendly: Friendly = FriendlyArray(["Foo", "Bar"])
(You would likely want to extend FriendlyArray to be a CollectionType.)
For a tale of my own descent into the madness of trying to make this work, and my crawl back from the edge, see NSData, My Old Friend.
The good news is that what you are asking for Conditional Conformance is coming in Swift 4.1:
https://swift.org/blog/conditional-conformance/

Resources