I'm trying to write a function to unwrap optionals with an arbitrary number of levels of nesting. Here's the test I'm using:
let a: Int??? = 1
let b: Int??? = nil
print(a.unwrap(0), b.unwrap(0)) // should print 1, 0
I can get the correct output with a basic generic function:
extension Optional {
func unwrap<T> (_ defaultValue: T) -> T {
return (self as? T) ?? defaultValue
}
}
print(a.unwrap(0), b.unwrap(0)) // 1, 0
But this doesn't prevent the function from being called with a different type than the optional. For instance, I could call a.unwrap("foo") and it would print "foo" instead of "1" since of course you can't cast Int??? to String.
I tried it using Wrapped instead, which semi-properly restricts the default value but doesn't give the correct output:
extension Optional {
func unwrap (_ defaultValue: Wrapped) -> Wrapped {
return (self as? Wrapped) ?? defaultValue
}
}
print(a.unwrap(0), b.unwrap(0)) // Optional(Optional(1)), nil
It only unwraps one level of the optional, instead of all three, and since nil is a valid value for Int?? it doesn't return the default.
Is there any way to safely do what I want here?
This code does what you ask for. The drawback is that you need to implement the Unwrappable protocol in every type you want to put inside optionals. Maybe Sourcery can help with that.
protocol Unwrappable {
associatedtype T
func unwrap(_ default: T) -> T
}
extension Optional {}
extension Optional: Unwrappable where Wrapped: Unwrappable {
typealias T = Wrapped.T
func unwrap(_ defaultValue: T) -> T {
if let value = self {
return value.unwrap(defaultValue)
}
return defaultValue
}
}
extension Int: Unwrappable {
typealias T = Int
func unwrap(_ default: Int) -> Int {
return self
}
}
let nestedOptionalValue: Int??? = 6
let nestedOptionalNil: Int??? = nil
let optionalValue: Int? = 6
let optionalNil: Int? = nil
print(nestedOptionalValue.unwrap(0)) // prints 6
print(nestedOptionalNil.unwrap(0)) // prints 0
print(optionalValue.unwrap(0)) // prints 6
print(optionalNil.unwrap(0)) // prints 0
The trick is that the Unwrappable protocol marks types that can be eventually unwrapped (as in after 0, 1 or more unwraps) to a certain type.
The difficulty in this problem comes from the fact that in an extension to Optional, you can get the Wrapped type, but if Wrapped is optional again, you can't access Wrapped.Wrapped (in other words, Swift doesn't support Higher Kinded Types).
Another approach, would be to try to add an extension to Optional where Wrapped == Optional. But again you'd need Higher Kinded Types support to access the Wrapped.Wrapped type. If we try to extend Optional where Wrapped == Optional<T>, we also fail, because we cant extend Optional generically over T.
Related
I am using Xcode Version 11.3.1 (11C504)
I am trying to create a generic function in Swift that will reject its
parameter unless such a parameter is Optional.
In the following code, I was expecting the system to report errors in all calls to onlyCallableByAnOptable() made inside test(), because none of them provide an optional value as a parameter.
However, the system only reports non-protocol conformance if I remove the Optional extension that conforms to Optable!
Which to me, it means that the system is regarding any and all values as Optional, regardless!
Am I doing something wrong?
(By the way, the following code used to be working as expected in earlier versions of Swift. I just recently found out that it stopped working, for it was letting a non-Optional go through.)
protocol Optable {
func opt()
}
func onlyCallableByAnOptable<T>( _ value: T) -> T where T: Optable {
return value
}
// Comment the following line to get the errors
extension Optional: Optable { func opt() {} }
class TestOptable {
static func test()
{
let c = UIColor.blue
let s = "hi"
let i = Int(1)
if let o = onlyCallableByAnOptable(c) { print("color \(o)") }
//^ expected ERROR: Argument type 'UIColor' does not conform to expected type 'Optable'
if let o = onlyCallableByAnOptable(s) { print("string \(o)") }
//^ expected ERROR: Argument type 'String' does not conform to expected type 'Optable'
if let o = onlyCallableByAnOptable(i) { print("integer \(o)") }
//^ expected ERROR: Argument type 'Int' does not conform to expected type 'Optable'
}
}
Since you've made all Optionals conform to Optable and you are using the if let syntax to unwrap the result of the call to onlyCallableByAnOptable (which means the return type must be some kind of Optional, which means the parameter must also be that same type of Optional because both the parameter and the return type are of type T in your generic method), Swift is inferring the types being passed in as UIColor?, String?, and Int? (implicitly wrapping them in Optionals) instead of UIColor, String and Int.
I am the one who posted this question.
I was trying to create a generic function in Swift that would reject
its parameter unless such parameter is an Optional.
As #TylerTheCompiler pointed out, using my original implementation (in the question), Swift was inferring type T (used in onlyCallableByAnOptable()), based on the full context of the call, not solely on the type of the value provided as parameter to it, therefore inferring T to be an Optional.
For the sake of helping others who might be trying to achieve the same as I was, the following is my solution to the problem I had.
All calls to onlyCallableByAnOptable(...) now correctly yield errors due to non-protocol conformance.
Errors like: Argument type 'UIColor' does not conform to expected type 'Optable'
If anyone knows of a simpler solution, please do post it as an answer
to: How to create a generic function in Swift that will reject the given parameter unless it is an Optional?.
protocol Optable {
associatedtype OptableType
func optionalOptable() -> OptableType?
func opt()
}
func onlyCallableByAnOptable<T>( _ value: T) -> T.OptableType? where T: Optable {
return value.optionalOptable()
}
extension Optional: Optable {
typealias OptableType = Wrapped //: Wrapped is the type of the element, as defined in Optional
func opt() {}
func optionalOptable() -> OptableType? {
return self
}
}
class TestOptable {
static func test()
{
let c = UIColor.blue
let s = "hi"
let i = Int(1)
if let o = onlyCallableByAnOptable(c) { // ERROR, as was desired.
print("color \(o)")
}
if let o = onlyCallableByAnOptable(s) { // ERROR, as was desired.
print("string \(o)")
}
if let o = onlyCallableByAnOptable(i) { // ERROR, as was desired.
print("integer \(o)")
}
}
}
Using Xcode 10, but did not migrate to Swift 4.2, so my project is still running with Swift 4.1.
Lets assume i have the following extension on Dictionary:
extension Dictionary where Key: ExpressibleByStringLiteral {
func find<T>(key: Key) -> T? {
return self[key] as? T
}
}
I use this function to access values in a hashmap in a type safe manner like:
let dict: [String: Any] = ["foo": "bar"]
let foo: String? = dict.find(key: "foo") // prints "bar"
My problem surfaces, when i would like to have Any type returned from my find function, like:
let bar: Any? = dict.find(key: "bar")
Pre Xcode 10, this function used to return to me plain and simple nil if the key was not found in the hashmap.
However, post Xcode 10, it returns Optional.some(nil).
I understand, that Any types can be initialised like the following:
let foo: Any = Optional<String>.none
I guess in my case something similar happens. Does anybody has an idea, how to work around it and still return nil from the find function?
This is due to an intentional change (#13910) where the compiler is now more conservative with unwrapping an optional value that's being cast to a generic placeholder type. Now, the results you get are more consistent with those that you would get in a non-generic context (see SR-8704 for further discussion).
For example:
// note the constraint `where Key : ExpressibleByStringLiteral` is needlessly restrictive.
extension Dictionary where Key : ExpressibleByStringLiteral {
func find<T>(key: Key) -> T? {
return self[key] as? T
}
}
let dict: [String: Any] = ["foo": "bar"]
let genericBar: Any? = dict.find(key: "bar")
print(genericBar as Any) // in Swift 4.1: nil, in Swift 4.2: Optional(nil)
// `T` in the above example is inferred to be `Any`.
// Let's therefore substitute `as? T` with `as? Any`.
let nonGenericBar = dict["bar"] as? Any
print(nonGenericBar as Any) // in both versions: Optional(nil)
As you can see, you now get Optional(nil) regardless of whether a generic placeholder was used in order to perform the cast, making the behaviour more consistent.
The reason why you end up with Optional(nil) is because you're performing a conditionally casting an optional value. The conditional cast on its own results in an optional value in order to indicate success or failure, and putting the resulting optional value in the success case gives you a doubly wrapped optional. And because you're casting to Any, which can represent an Optional value, no unwrapping needs to be done in order to "fit" the value in the resultant type.
If you wish to flatten the resulting optional into a singly wrapped optional, you can either coalesce nil:
extension Dictionary {
func find<T>(key: Key) -> T? {
return (self[key] as? T?) ?? nil
}
}
Or unwrap the value before casting, for example using a guard:
extension Dictionary {
func find<T>(key: Key) -> T? {
guard let value = self[key] else { return nil }
return value as? T
}
}
or, my preferred approach, using flatMap(_:):
extension Dictionary {
func find<T>(key: Key) -> T? {
return self[key].flatMap { $0 as? T }
}
}
That all being said, I often find the usage of [String: Any] to be a code smell, strongly indicating that a stronger type should be used instead. Unless the keys can really be arbitrary strings (rather than a fixed set of statically known keys), and the values can really be of any type for a particular key – there's almost certainly a better type you can be using to model your data with.
In my simplified example I'm getting the error: Cannot convert value of type 'Foo' to expected argument type BaseItem<Any>
But the class Foo extends BaseItem<String>.
This is the example code:
class BaseItem<T> {
var param: T?
}
class Foo: BaseItem<Int> {
}
func checkItem(item: BaseItem<Any>) -> Bool{
return item.param != nil;
}
I get the error when calling
checkItem(item: Foo())
What am I missing?
You need to define your checkItem function in terms of generics too:
func checkItem<T>(item: BaseItem<T>) -> Bool {
return item.param != nil
}
Gotta define checkItem function with generics too.
func checkItem<T>(item: BaseItem<T>) -> Bool {
return item.param != nil
}
The problem is that generics are invariant – consider if your checkItem(item:) function had said:
func checkItem(item: BaseItem<Any>) {
item.param = "foo"
}
That would be illegal for a BaseItem<Int>, as you cannot possibly assign a String instance to an Int? property – which is why it (an instance of Foo) cannot be typed as a BaseItem<Any>.
The solution, as other answers have said, is to use a generic placeholder for the function:
func checkItem<T>(item: BaseItem<T>) -> Bool {
return item.param != nil
}
Now, rather than saying that you're taking a BaseItem<Any>, that has a param of type Any? (can be assigned a value of any type) – you're now saying that you're taking a BaseItem with any specific placeholder type; which will be satisfied at the call-site of the function.
The function implementation itself therefore cannot make any assumptions about this type, and will disallow the assignment of an arbitrary value to param. The compiler will only allow an assignment of a value of type T.
The signature of checkItem function should be: checkItem<T>(item: BaseItem<T>) -> Bool, as follows:
func checkItem<T>(item: BaseItem<T>) -> Bool {
return item.param != nil
}
Usage:
checkItem(item: Foo()) // false
let myFoo = Foo()
myFoo.param = 0
checkItem(item: myFoo) // true
The reason of why the compiler complains about
Cannot convert value of type 'Foo' to expected argument type
BaseItem
is that you are trying to pass BaseItem<Int> instance as BaseItem<Any> which is invalid (Any data type is not T generic type).
I'm trying to get an unwrapped type from an optional type in runtime.
The following code would print the type of a as Optional<String>.
class MySubClass: MyClass {
var a: String? = nil
}
var a = MySubClass()
let mirror = Mirror(reflecting: a)
for child in mirror.children {
print(child.value.dynamicType)
}
Now I want to unwrap the type and get String, what should I do to make this happen in runtime?
Assuming you have an optional
let someVar: String?
then print(type(of: someVar)) will print
Optional<String>
but if you add the following extension to Optional
protocol OptionalProtocol {
func wrappedType() -> Any.Type
}
extension Optional: OptionalProtocol {
func wrappedType() -> Any.Type {
return Wrapped.self
}
}
then print(someVar.wrappedType()) will print
String
No reflection whatsoever
Summary
As long as the optional is not referenced by Any or AnyObject the code will work fine.
For Any you will have to cast it to OptionalProtocol first. Running
let someVar: String?
let anyVar = someVar as Any
if let op = anyVar as? OptionalProtocol {
print(op.wrappedType())
}
will print
String
As for AnyObject, strangely enough (at least for me), it doesn't cast to OptionalProtocol.
The original StackOverflow answer can be found here
I played with your idea a little bit, but I think there isn't a real way to do that, since you can't get the type of the associated value of an enumeration, yet. Hence Optionals are basically Enumerations, we have a problem here.
My idea would be to test for all possible value types in your model objects could be or hold. Like:
let myModelObject:Any? = someWayToGetTheData()
if let aString = myModelObject as? String {
// do everything you need to store a string
}
else if let anInteger = myModelObject as? Int {
// do everything you need to store an integer
}
// and so on ...
Since your json and your model must have a predefined number of supported conversions that is a possible way, and as far as I understand your original problem, it's basically as useful as testing for the dynamic associated value type of an Optional Enumeration, which will lead into a chain of if-else statements as well.
You can either unwrap the optional explicitly with a bang (!) or with an if let.
For example:
var foo: String? = nil
if foo == nil {
print("foo is nil")
foo = "bar"
}
let fooBang = foo!
print("fooBang: \(fooBang)")
if let ifLetFoo = foo {
print("ifLetFoo: \(ifLetFoo)")
}
This will print:
foo is nil
fooBang: bar
ifLetFoo: bar
In your context, I think print(child.value.dynamicType!) might be what you're looking for.
If you cast the value to the non-optional String, it will print you the unwrapped type.
let mirror = Mirror(reflecting: a)
for child in mirror.children {
print(String(child.value).dynamicType) //String
}
Or you can play with the String and get the type from the Optional type.
class MySubClass: MyClass {
var a: Int? = nil
}
var a = MySubClass()
let mirror = Mirror(reflecting: a)
for child in mirror.children {
let typeArr = String(child.value.dynamicType).characters.split{$0 == "<"}.map(String.init)
let typeArr2 = typeArr[1].characters.split{$0 == ">"}.map(String.init)
print(typeArr2[0]) // print: Int
}
Sorry this is lame but you can do something like this.
Swift allows us to use the shorthand notation str! to unwrap an optional. But what if we want to do the opposite?
Say I have a variable:
var str = String() // String
Is there any shorthand notation to convert this to an optional (i.e. String? or String!)?
(E.g. I want to do something like var strOptional = ?(str).)
Alternatively, if there is no shorthand for this notation, how can I convert it to an optional without explicitly mentioning its type (e.g. I don't want to mention String).
In other words, I know that I can wrap a variable as an optional with any of these methods:
var strOptional = str as String?
var strOptional: String? = str
var strOptional = String?(str)
... but in each case, I must explicitly write String.
I would rather write something like: var strOptional = str as typeof?(str), if there is no shorthand syntax. (The advantage is that if the variable's type is frequently changed in the code base, it would be one less place to update.)
As far as a real world example of where this would be useful, imagine I want to use an AVCaptureDevice and I use the following code:
let device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
device.lockForConfiguration(nil)
lockForConfiguration() will crash at runtime on a device that has no video camera, and the compiler won't warn me about it. The reason is that defaultDeviceWithMediaType may return nil according to the documentation[1], yet it is defined to return a AVCaptureDevice!.
To fix a faulty API like this, it would be nice to do something like:
let device = ?(AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo))
... to get a AVCaptureDevice?, and have the compiler catch any mistakes I might make.
Currently, I must resort to the more verbose:
let device: AVCaptureDevice? = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
Another example:
In this case, I want to give a default value to my variable that is a string, but later on, I may want to assign it a nil value.
var myString = "Hi"
// ...
if (someCondition()) {
myString = nil // Syntax error: myString is String
}
Currently I have to resort to var myString: String? = "Hi" but something like var myString = ?("Hi") would be less verbose.
[1] If you open AVCaptureDevice.h, you will see the following documentation about the return value: "The default device with the given media type, or nil if no device with that media type exists."
Optional is just an enum in swift, so you can do: Optional(str)
From the swift interface:
/// A type that can represent either a `Wrapped` value or `nil`, the absence
/// of a value.
public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
case None
case Some(Wrapped)
/// Construct a `nil` instance.
public init()
/// Construct a non-`nil` instance that stores `some`.
public init(_ some: Wrapped)
/// If `self == nil`, returns `nil`. Otherwise, returns `f(self!)`.
#warn_unused_result
public func map<U>(#noescape f: (Wrapped) throws -> U) rethrows -> U?
/// Returns `nil` if `self` is `nil`, `f(self!)` otherwise.
#warn_unused_result
public func flatMap<U>(#noescape f: (Wrapped) throws -> U?) rethrows -> U?
/// Create an instance initialized with `nil`.
public init(nilLiteral: ())
}
To wrap up a variable you could use operator overloading and generic functions. For example:
prefix operator ??? {}
prefix func ??? <T> (x: T) -> T? {
return Optional(x)
}
To relate this to your second example, you would do the following:
var myString = ???"Hi" // Optional("Hi")
if someCondition() {
myString = nil
}
In the lighthearted spirit that, as I understand, the question was posed, you can define a postfix operator that uses one character less than your ideal solution (a pair of parens and a question mark):
postfix operator =? {}
postfix func =? <T> (rhs: T) -> T? { return rhs }
postfix func =? <T> (rhs: T!) -> T? { return rhs ?? nil }
var x = 3=?
x is Int? //--> true
var imp: Int! = 3
var opt = imp=?
opt is Int? //--> true
As to Apple's APIs you mention, since they already return an optional, albeit an implicitly unwrapped optional, you could use the nil coalescing operator ?? to convert it to a plain optional:
let y: Int! = 3 // posing for a rogue API
y is Int? //--> false
let z = y ?? nil
z is Int? //--> true
Perhaps a sligtly more interesting use of a dodgy operator is a higher order operator function that lifts T -> Us into T? -> U?s...
func opt <T, U> (f: T -> U) -> T? -> U? { // we'll implement this as an operator
return { $0.map(f) }
}
postfix operator ->? {}
postfix func ->? <T, U> (f: T -> U) -> T? -> U? {
return { $0.map(f) }
}
Now, let's use it:
let square: Int -> Int = { $0 * $0 }
let i: Int? = 3
square->? (i)
Which is equivalent to:
opt(square)(i)
Which is equivalent to (but potentially a little more versatile than):
i.map(square)
This all makes much more sense when used along with the pipe forward operator:
infix operator |> { associativity left precedence 89 }
func |> <A, B>(a: A, f: A -> B) -> B {
return f(a)
}
Then you can do:
i |> opt(square)
or
i |> square->?
Or we can conflate these into an optional pipe forward, which can take care of implicitly unwrapped optionals too:
infix operator ?> { associativity left precedence 89 }
func ?> <T, U> (lhs: T?, rhs: T -> U) -> U? {
return lhs.map(rhs)
}
let im: Int! = 5
let op: Int? = 5
im ?> square |> debugPrintln //--> Optional(25)
op ?> square |> debugPrintln //--> Optional(25)
Which is equivalent to:
debugPrintln(op.map(square))