How does covariance work for Optionals in Swift?
Say I write the following code:
var nativeOptionalView: Optional<UIView>
let button = UIButton()
nativeOptionalView = .Some(button)
var nativeOptionalButton = Optional.Some(button)
nativeOptionalView = nativeOptionalButton
It compiles and works just fine. However if I define MyOptional as
enum MyOptional<T> {
case Some(T)
case None
}
And write the following:
var myOptionalView: MyOptional<UIView>
let button = UIButton()
myOptionalView = .Some(button)
var myOptionalButton = MyOptional.Some(button)
myOptionalView = myOptionalButton
I get the error:
error: cannot assign value of type 'MyOptional<UIButton>' to type 'MyOptional<UIView>'
I understand why this errors happens with MyOptional, what I don't understand is why it doesn't happen with Optional.
It doesn't. Swift does not support custom covariant generics for now.
The Swift type checker is per expression, not global (as in Haskell). This task is handled by the Semantic Analysis in lib/Sema. The constraint system then tries to match the types and special cases of covariance are then handled for collections, and optionals.
This was a language design decision. You should be able to do everything you need with the built-in collection types and optionals. If you aren't you should probably open a radar.
While I agree that there is probably some "compiler magic" going on, this can be accomplished in your custom implementation by casting the button to a UIView, e.g.
var myOptionalButton = MyOptional.Some(button as UIView)
or
var myOptionalButton: MyOptional<UIView> = .Some(button)
Related
So I want I my base tableView, upon which I've derived others, to centralize drag-n-drop operations. Each of these tableViews have a distinct array controller as its dataSource, with each item class conforming to pasteboard reading and writing protocols.
But I'm stuck on its setup
override func mouseDragged(with event: NSEvent) {
let arrayController = self.dataSource as! NSArrayController
let itemClass = arrayController.objectClass
let objects = arrayController.arrangedObjects as! [itemClass]
let indexSet = self.selectedRowIndexes
var items = [NSDraggingItem]()
for index in indexSet {
let item = NSDraggingItem.init(pasteboardWriter: objects[index])
items.append(item)
}
self.beginDraggingSession(with: items, event: event, source: self)
}
as I get an error ?
Short Answer: This just isn't how Swift works, cast (as? or as!) to an appropriate compile time type – in this case from the use of objects in NSDraggingItem.init(pasteboardWriter: objects[index]) that is probably [NSPasteBoardWriting]
Longer Answer:
You may have just made a simple error, we all do sometimes, and the short answer is enough. But if you are wondering why your code isn't accepted maybe this will help and hopefully not confuse!
What you are trying to do is a form of dynamic typing, you set itemClass to a type that at compile time nothing is known about except that it is some class type.
At its core Swift is essentially a statically typed language, it works by either knowing everything about the type of something, e.g. when something is declared to have a particular reference or value type; by knowing something about the type, e.g. when something has a protocol type; or even nothing, e.g. when something is of unconstrained generic parameter type and in all these cases what can be done is largely limited by this knowledge.
Variable types are not supported; while there are less specific types, e.g. the AnyClass in the example, operations specific to the actual runtime type of something cannot be performed until a cast to that specific (compile time known) type is made (with as? or as!)
Swift does support some dynamic typing features, notably in its support for parts of Objective-C – which has both statically and dynamically typed parts; but these do not give you what you are trying to do here, that is cast to a type unknown until runtime.
You probably know Objective-C, a simple example of how the two languages differ in static/dynamic approach is what happens when a method/function is called. For Objective-C method dispatch is dynamic, the compiled code performs a search for the implementation of the method and that search may fail resulting in a runtime error and abort. In Swift (excluding its interworking with Objective-C) when a function is called the compiled code does not search, the implementation is known to exist at compile time and there can by no runtime error and abort.
To do what you are attempting you need to determine at design time what type you need to perform the desired operation (construct an NSDraggingItem in this case) and whether you either absolutely know or maybe know the value you have will at runtime conform to that type. If you absolutely know you can use the as! cast, which will abort execution if you are wrong, or you can use the as? which allows you to test for success and take appropriate action if you have something of unplanned type.
HTH
I will posit my own answer with the Sweeper's suggestion and CRD's excellent commentary.
The source objects, conform to the pasteboard writing protocol - NSPasteboardWriting; I mandate it. The target casting confirms this. My issue was a noob case of thinking objC, and cast at source vs target but enforcing just the same; I hope ;-).
override func mouseDragged(with event: NSEvent) {
let arrayController = self.dataSource as! NSArrayController
let objects = arrayController.arrangedObjects as! [NSPasteboardWriting]
let delegate = self.delegate as! NSViewController
let indexSet = self.selectedRowIndexes
var items = [NSDraggingItem]()
for index in indexSet {
let dragImage = (delegate.view.window?.windowController?.document as! Document).displayImage!
let item = NSDraggingItem.init(pasteboardWriter: objects[index])
item.setDraggingFrame(self.rect(ofRow: index), contents: dragImage)
item.draggingFrame = self.rect(ofRow: index)
items.append(item)
}
self.beginDraggingSession(with: items, event: event, source: self)
}
I want to store a more specialized type in a Dictionary of type [String:SomeClass]. Here is some sample code illustrating my problem (also available to play with at https://swiftlang.ng.bluemix.net/#/repl/579756cf9966ba6275fc794a):
class Thing<T> {}
protocol Flavor {}
class Vanilla: Flavor {}
var dict = [String:Thing<Flavor>]()
dict["foo"] = Thing<Vanilla>()
It produces the error ERROR at line 9, col 28: cannot assign value of type 'Thing<Vanilla>' to type 'Thing<Any>?'.
I've tried casting Thing<Vanilla>() as Thing<Flavor> but that produces the error cannot convert value of type 'Thing<Vanilla>' to type 'Thing<Flavor>' in coercion.
I've also tried to define the Dictionary as type [String:Thing<Any>] but that doesn't change anything either.
How do I create a collection of different Things without resorting to plain [String:AnyObject]?
I should also mention that the class Thing is not defined by me (in fact it's about BoltsSwift Tasks), so the solution to create a base class of Thing without a type parameter doesn't work.
A Thing<Vanilla> is not a Thing<Flavor>. Thing is not covariant. There is no way in Swift to express that Thing is covariant. There are good reasons for this. If what you were asking for were allowed without careful rules around it, I would be allowed to write the following code:
func addElement(array: inout [Any], object: Any) {
array.append(object)
}
var intArray: [Int] = [1]
addElement(array: &intArray, object: "Stuff")
Int is a subtype of Any, so if [Int] were a subtype of [Any], I could use this function to append strings to an int array. That breaks the type system. Don't do that.
Depending on your exact situation, there are two solutions. If it is a value type, then repackage it:
let thing = Thing<Vanilla>(value: Vanilla())
dict["foo"] = Thing(value: thing.value)
If it is a reference type, box it with a type eraser. For example:
// struct unless you have to make this a class to fit into the system,
// but then it may be a bit more complicated
struct AnyThing {
let _value: () -> Flavor
var value: Flavor { return _value() }
init<T: Flavor>(thing: Thing<T>) {
_value = { return thing.value }
}
}
var dict = [String:AnyThing]()
dict["foo"] = AnyThing(thing: Thing<Vanilla>(value: Vanilla()))
The specifics of the type eraser may be different depending on your underlying type.
BTW: The diagnostics around this have gotten pretty good. If you try to call my addElement above in Xcode 9, you get this:
Cannot pass immutable value as inout argument: implicit conversion from '[Int]' to '[Any]' requires a temporary
What this is telling you is that Swift is willing to pass [Int] where you ask for [Any] as a special-case for Arrays (though this special treatment isn't extended to other generic types). But it will only allow it by making a temporary (immutable) copy of the array. (This is another example where it can be hard to reason about Swift performance. In situations that look like "casting" in other languages, Swift might make a copy. Or it might not. It's hard to be certain.)
One way to solve this is adding an initialiser to Thing and creating a Thing<Flavor> that will hold a Vanilla object.
It will look something like:
class Thing<T> {
init(thing : T) {
}
}
protocol Flavor {}
class Vanilla: Flavor {}
var dict = [String:Thing<Flavor>]()
dict["foo"] = Thing<Flavor>(thing: Vanilla())
Let's say I have something like this
struct A {
lazy var b: String = { return "Hello" }()
}
If I try to reflect struct A and access the value for b through its MirrorType like so:
var a = A()
var r = reflect(a)
for i in 0..r.count {
let (n, m) = r[i]
println("\(m.value)")
var c = a.b
println("\(m.value)")
}
I get nil in the console both times. Note that the underlying value type is Swift.Optional<Swift.String>, and the variable name is somewhat confusingly b.storage. Is there a way to access the underlying value of a lazy-loaded variable using reflection or initialize it from its MirrorType or am I stuck waiting for someone to write a first-class reflection api for Swift?
The MirorType is very limited in it's functionality. Besides that it's replaced by other functionality in Xcode 7 beta 4.
The point in your case is that the property has not been used yet. So it's actually still nil. The only way to make it not nil is by accessing the property by getting it's value. Unfortunately in Swift you can not do that by executing .valueForKey("propertyName")
If you are looking for a reflection library that is trying to get as much as possible out of Swift, then have a look at EVReflection
In c++, one can introduce an alias reference as follows:
StructType & alias = lengthyExpresionThatEvaluatesToStuctType;
alias.anAttribute = value; // modify "anAttribute" on the original struct
Is there a similar syntactic sugar for manipulating a (value typed) struct in Swift?
Update 1: For example: Let say the struct is contained in a dictionary of kind [String:StructType], and that I like to modify several attributes in the the struct myDict["hello"]. I could make a temporary copy of that entry. Modify the copy, and then copy the temporary struct back to the dictionary, as follows:
var temp = myDict["hello"]!
temp.anAttribute = 1
temp.anotherAttribute = "hej"
myDict["hello"] = temp
However, if my function has several exit points I would have to write myDict["hello"] = temp before each exit point, and it would therefore be more convinient if I could just introduce and alias (reference) for myDict["hello"] , as follows:
var & alias = myDict["hello"]! // how to do this in swift ???
alias.anAttribute = 1
alias.anotherAttribute = "hej"
Update 2: Before down- or close- voting this question: Please look at Building Better Apps with Value Types in swift (from WWWDC15)!! Value type is an important feature of Swift! As you may know, Swift has borrowed several features from C++, and value types are maybe the most important feature of C++ (when C++ is compared to Java and such languages). When it comes to value types, C++ has some syntactic sugar, and my questions is: Does Swift have a similar sugar hidden in its language?. I am sure Swift will have, eventually... Please, do not close-vote this question if you do not understand it!
I have just read Deitel's book on Swift. While I'am not an expert (yet) I am not completely novel. I am trying to use Swift as efficient as possible!
Swift doesn't allow reference semantics to value types generally speaking, except when used as function parameters declared inout. You can pass a reference to the struct to a function that works on an inout version (I believe, citation needed, that this is implemented as a copy-write, not as a memory reference). You can also capture variables in nested functions for similar semantics. In both cases you can return early from the mutating function, while still guaranteeing appropriate assignment. Here is a sample playground that I ran in Xcode 6.3.2 and Xcode 7-beta1:
//: Playground - noun: a place where people can play
import Foundation
var str = "Hello, playground"
struct Foo {
var value: Int
}
var d = ["nine": Foo(value: 9), "ten": Foo(value: 10)]
func doStuff(key: String) {
let myNewValue = Int(arc4random())
func doMutation(inout temp: Foo) {
temp.value = myNewValue
}
if d[key] != nil {
doMutation(&d[key]!)
}
}
doStuff("nine")
d // d["nine"] has changed... unless you're really lucky
// alternate approach without using inout
func doStuff2(key: String) {
if var temp = d[key] {
func updateValues() {
temp.value = Int(arc4random())
}
updateValues()
d[key] = temp
}
}
doStuff2("ten")
d // d["ten"] has changed
You don't have to make the doMutation function nested in your outer function, I just did that to demonstrate the you can capture values like myNewValue from the surrounding function, which might make implementation easier. updateValues, however, must be nested because it captures temp.
Despite the fact that this works, based on your sample code, I think that using a class here (possibly a final class if you are concerned about performance) is really more idiomatic imperative-flavored Swift.
You can, if you really want to, get a raw pointer using the standard library function withUnsafeMutablePointer. You can probably also chuck the value into an inner class that only has a single member. There are also functional-flavored approaches that might mitigate the early-return issue.
Can someone please explain why if, as it says in the import definition:
typealias SKColor = UIColor
I get the error 'UIColor!' is not identical to 'SKColor' when I do the following? I was about to say 'I know the difference between UIColor and UIColor!' but actually, maybe I don't truly understand!
import UIKit
import SpriteKit
func nColours(gradient: [SKColor]) -> Int {
return gradient.count
}
let gradient = [SKColor.redColor(), SKColor.magentaColor()]
nColours([SKColor.redColor(), SKColor.magentaColor()]) // 2, OK
nColours(gradient) // <<<<<< Error
// 'UIColor!' is not identical to 'SKColor'
experimenting, I tried this :
let gradient = [SKColor.redColor(), SKColor.magentaColor()]
let b = (gradient == [SKColor.redColor(), SKColor.magentaColor()]) // <<<<<< Error
// '[UIColor!]' is not convertible to '_ArrayCastKind'
The problem is that UIColor and UIColor! are technically different types - the ! stands for Implicitly Unwrapped Optional and is used where there is an optional value, but that optional should always have a value.
It seems that many objects returned from the system frameworks use implicitly optional types, though this usage should get less common as the frameworks are fully converted to Swift - there's no reason UIColor.redColor() would ever return a nil color, so its return type will probably change from SKColor! to SKColor in the future.
In your code example, if you change your let gradient declaration to explicitly declare the array as being of type [SKColor] (as opposed to SKColor!) the compiler happily carries on:
let gradient : [SKColor] = [SKColor.redColor(), SKColor.magentaColor()]